简单工厂模式

2021-05-07 10:19:38 浏览数 (1)

  • 简单工厂模式概述

  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)是没有问题的。

参考文章

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

0 人点赞