前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简单工厂模式

简单工厂模式

原创
作者头像
大龄老码农-昊然
修改2021-05-07 10:19:38
4300
修改2021-05-07 10:19:38
举报
  • 简单工厂模式概述

  1. 定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类
  2. 简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被成为静态工厂方法(Static Factory Method)
  3. 需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其实现过程

  1. 例如,我开一家披萨店,当客户需要某种披萨并且我这家店里也能做的时候,我就会为其提供所需要的披萨(当然是要钱的哈哈),如果其所需的我这没有,则是另外的情况,后面会谈。这时候,我这家 披萨店就可以看做工厂(Factory),而生产出来的披萨被成为产品(Product),披萨的名称则被称为参数,工厂可以根据参数的不同返回不同的产品,这就是简单工厂模式
  • 简单工厂模式的结构与实现

abstract class Product

{

public void MethName()

{

//公共方法的实现

}

public abstract void MethodDiff();

//声明抽象业务方法

}

class ConcreteProductA : Product

{

public override void MethodDiff()

{

//业务方法的实现

}

}

class Factory

{

public static Product GetProduct(string arg)

{

Product product = null;

if(arg.Equals("A")

{

product = new ConcreteProductA();

//init

}

else if(arg.Equals("B"))

{

product = new ConcreteProductB();

//init

}

else

{

....//其他情况

}

return product;

}

}

class Program

{

static void Main(string[] args)

{

Product product;

product = Factory.GetProduct("A");//工厂类创建对象

Product.MethName();

product.MethodDiff();

}

}

  • 简单工厂模式的简化

  1. 为了简化简单工厂模式,将抽象产品类和工厂类合并,将静态工厂方法移到抽象产品类中

  1. 客户端可以调用产品父类的静态工厂方法,根据不同的参数创建不同类型的产品子类对象。
  • 简单工厂模式的优缺点和适用环境  

  • 简单工厂模式的优点

    (1)工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责     (2)客户端无需知道所创建具体产品的类名,只需知道参数即可     (3)也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。(这也是我在开始的披萨店里遇到没有的披萨的解决情况)

  • 简单工厂模式的缺点

    (1)工厂类集中了所有产品的创建逻辑,职责过重,一旦异常,整个系统将受影响     (2)使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度     (3)系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂     (4)简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构。

  • 简单工厂模式的适用环境

    (1)工厂类负责创建对的对象比较少,因为不会造成工厂方法中的业务逻辑过于复杂

    (2)客户端只知道传入工厂类的参数,对如何创建对象不关心

  • 简单案例

  1. 题目: 使用简单工厂模式设计一个可以创建不同几何图形(Shape),如Circle,Rectangle,Triangle等绘图工具类,每个几何图形均具有绘制Draw()和擦除Erase()两个方法 要求在绘制不支持的几何图形时,抛出一个UnsuppShapeException异常,绘制类图并使用C#语言实现。
  2. UML

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

/*使用简单工厂模式设计一个可以创建不同几何图形(Shape),如Circle,Rectangle,Triangle等绘图工具类,每个几何图形均具有绘制Draw()和擦除Erase()两个方法

* 要求在绘制不支持的几何图形时,抛出一个UnsuppShapeException异常,绘制类图并使用C#语言实现。

*/

namespace SimpleShapeFactory

{

public interface InShape//图形接口 抽象产品类

{

void Draw();

void Erase();

}

public class Circle : InShape//圆形类,具体产品类

{

private static int count; //生成图形计数

string radious;

public Circle()//构造

{

Console.WriteLine("Create Circle");

Console.WriteLine("Input the radious of Circle:");

radious = Console.ReadLine();

}

public void Draw()//实现接口方法

{

int Radious = int.Parse(radious);

Console.WriteLine("Display circle " + (++count) +" information:");

Console.WriteLine("Circle "+ count+ " circumference:" + 2 * Radious * 3.14159);

Console.WriteLine("Circle "+ count+" area:" + 3.14159 * Radious * Radious);

}

public void Erase()//实现接口方法

{

while (true)

{

Console.WriteLine("Erase current shape(y/n)?");

string choose;

choose = Console.ReadLine();

if (choose.Equals("y") || choose.Equals("Y"))

{

Console.WriteLine("Erase Circle "+count +" successfully!");

count--;

break;

}

else if (choose.Equals("n") || choose.Equals("N"))

{

Console.WriteLine("Circle "+ count+" successfully saved!");

break;

}

else

{

Console.WriteLine("Input error, re-enter!");

}

}

}

}

class Rectangle : InShape//矩形类,具体产品类

{

private static int count = 0;//生成图形计数

string length;

string wideth;

public Rectangle()//构造

{

Console.WriteLine("Create Rectangle");

Console.WriteLine("Input the length and wideth of Rectangle:");

length = Console.ReadLine();

wideth = Console.ReadLine();

}

public void Draw()//实现接口方法

{

int Length = int.Parse(length);

int Wideth = int.Parse(wideth);

Console.WriteLine("Display rectangle " + (++count) + " information:");

Console.WriteLine("Rectangle "+ count + "circumference:" + 2 * Length * Wideth);

Console.WriteLine("Rectangle "+ count + "area:" + Length * Wideth);

}

public void Erase()//实现接口方法

{

while (true)

{

Console.WriteLine("Erase current shape(y/n)?");

string choose;

choose = Console.ReadLine();

if (choose.Equals("y") || choose.Equals("Y"))

{

Console.WriteLine("Erase rectangle "+count+ "successfully!");

--count;

break;

}

else if (choose.Equals("n") || choose.Equals("N"))

{

Console.WriteLine("Rectangle "+ count+" successfully saved!");

break;

}

else

{

Console.WriteLine("Input error, re-enter!");

}

}

}

}

class Triangle : InShape//三角形类,具体产品类

{

private static int count = 0;//生成图形计数

string lengtha;

string lengthb;

string lengthc;

public Triangle()//构造

{

Console.WriteLine("Create Triangle");

Console.WriteLine("Input the lengtha ,lengthb and lengthc of Triangle:");

lengtha = Console.ReadLine();

lengthb = Console.ReadLine();

lengthc = Console.ReadLine();

}

public void Draw()//实现接口方法

{

int Lengtha = int.Parse(lengtha);

int Lengthb = int.Parse(lengthb);

int Lengthc = int.Parse(lengthc);

if ((Lengtha + Lengthb > Lengthc) && (Lengtha + Lengthc > Lengthb) && (Lengthb + Lengthc > Lengtha))

{

double S = (Lengtha + Lengthb + Lengthc) * 0.5;

double area = Math.Sqrt(S * (S - Lengtha) * (S - Lengthb) * (S - Lengthc));

Console.WriteLine("Display triangle "+ (++count)+" information:");

Console.WriteLine("Triangle " + count +" circumference:" + (Lengtha + Lengthb + Lengthc));

Console.WriteLine("Triangle "+ count +" area:" + area);

Erase();

}

else

{

Console.WriteLine("Create triangle failed!");

}

}

public void Erase()//实现接口方法

{

while (true)

{

Console.WriteLine("Erase shape(y/n)?");

string choose;

choose = Console.ReadLine();

if (choose.Equals("y") || choose.Equals("Y"))

{

Console.WriteLine("Erase tirangle " +count +" successfully!");

--count;

break;

}

else if (choose.Equals("n") || choose.Equals("N"))

{

Console.WriteLine("Triangle "+ count +" successfully saved!");

break;

}

else

{

Console.WriteLine("Input error, re-enter!");

}

}

}

}

class ShapeFactory//图形工厂类,充当工厂类

{

public static InShape Getshape(string type)//静态工厂方法

{

InShape shape;

shape = null;

if (type.Equals("Circle"))

{

shape = new Circle();

Console.WriteLine("Init set Circle");

shape.Draw();

shape.Erase();

}

else if(type.Equals("Rectangle"))

{

shape = new Rectangle();

Console.WriteLine("Init set Rectangle");

shape.Draw();

shape.Erase();

}

else if (type.Equals("Triangle"))

{

shape = new Triangle();

Console.WriteLine("Init set Triangle");

shape.Draw();

}

else//异常 这里我应该声明调用异常处理类的,那样会更好些

{

Console.WriteLine("UnsupportShapeException!");

Console.WriteLine("Emotional reminders :Pay 1 million$ to Alipay:132****6151 can create every shape you want!!! ");

}

return shape;

}

}

class Program//客户端测试类

{

static void Main(string[] args)

{

while (true)

{

InShape shape;

Console.WriteLine("Please input the shape you want to create");

string str = Console.ReadLine();

shape = ShapeFactory.Getshape(str);//通过静态工厂方法创建产品

Console.ReadLine();

}

}

}

}

优点和缺点

3.1 优点

  • 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
  • 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

3.2 缺点

  • 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
  • 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,同样破坏了“开闭原则”;在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护
  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构

4 适用环境

在以下情况下可以使用简单工厂模式:

  • 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  • 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数

模式应用

  • JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。 public final static DateFormat getDateInstance(); public final static DateFormat getDateInstance(int style); public final static DateFormat getDateInstance(int style,Locale locale);

获取不同加密算法的密钥生成器。

代码语言:javascript
复制
KeyGenerator keyGen=KeyGenerator.getInstance("DESede");

违背开闭原则

对于上面两种简单工厂模式的实现方法,如果我们要添加新的 parser,那势必要改动到 RuleConfigParserFactory 的代码,那这是不是违反开闭原则呢?实际上,如果不是需要频繁地添加新的 parser,只是偶尔修改一下 RuleConfigParserFactory 代码,稍微不符合开闭原则,也是完全可以接受的。

尽管简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加 parser,也没有太多的 parser)是没有问题的。

参考文章

更多相关知识和参考文章来源可以关注我的博客站点

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 优点和缺点
    • 3.1 优点
      • 3.2 缺点
      • 4 适用环境
      • 违背开闭原则
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档