我的设计模式系列----教你读懂工厂玩法
一、时常听到各种工厂,解读工厂
很多人大概从开始工作起,逐渐接触听说过各项工厂类名词,什么“简单工厂模式”啊,什么“静态工厂方法”啊,什么“工厂模板”模式啊,什么“抽象工厂方法”啊,等等
时常听着一头雾水,或者一时半知半解,甚至觉得怎么听着在玩概念,故弄玄虚一样。明明听着有什么联系,但又理不清头绪似的。
其实:
简单工厂模式(Simple Factory):简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。[百度百科】
工厂方法模式(Factory Method):是一种常用的对象创建型设计模式,此模式的核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品【百度百科】
抽象工厂模式(Abstract Factory):抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。根据里氏替换原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。【百度百科】
二、3种工厂的意图及UML和优缺点分析
简单工厂 : 在工厂类中由用户操作传入相关参数来动态决定生产那种具体产品实例的过程
目的是为了封装动态生成具体产品实例的过程,使用多态定义N种具体产品实例,而在用户选择时动态决定最后去创建那种具体产品实例的过程。
优点:封装了工厂类中相关判断逻辑,解除了客户端与具体产品的耦合,客户端只要给对应选择条件,工厂类会根据对应条件动态实例化具体产品类。
缺点:当突然有新的具体产品引入的时候除需要引入新的产品实例类,还需要变更工厂类中动态生产具体产品实例的部分,违反了软件设计之开-闭原则
工厂方法模式 :定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
主要是为了解决简单工厂中因新产品加入时变更工厂类逻辑而违背开闭原则的设计
优点:将具体产品的实例化延迟至具体工厂中完成,在子类中决定实例化。这样当有新产品引入时不用变更工厂逻辑,而只需要添加新的具体工厂即可。解决了简单工厂中破坏的开-闭原则的问题
每个具体工厂只负责具体产品的实例化,符合软件设计的单一职责原则
缺点:当引入具体产品时,会引入具体工厂子类,增加了代码的复杂度
当工厂改产品线由原只生产A变更为只生产B时,仍然需要变更产品B的具体工厂类
一个具体工厂只能生产一个具体的产品
抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
主要为了解决工厂方法中一个具体工厂只能实例化一个具体产品的缺陷
优点:相对工厂方法模式,抽象工厂模式支持某个产品序列,而工厂方法只针对某一具体产品
当需要变更产品系列(多个产品)时,只需要变更具体工厂类即可。改动非常小且非常容易,符合开闭原则
客户端与具体实例的创建完全分离,整个产品创建过程只与具体工厂的接口定义有关(而与是那个具体产品无关),典型面向接口而非面向实现,符合这依赖倒置原则。
缺点:新增加具体产品时,改动会非常大,需要增加一个抽象产品类及该产品类的实现,同时还需要更改抽象工厂类(添加生产该产品的方法)以及具体工厂类(添加生产该产品的方法实现)。
三、各工厂对应代码示例解析
1. 简单工厂
主要角色:简单工厂、抽象产品、具体产品、Client客户端
还是以之前计算2个数的结果的示例来看:
简单工厂:根据用户的算法符号类型返回对应的算法(简单工厂中即为在工厂类中根据用户的参数动态生成对应的产品实例)
1 /// <summary> 2 /// 简单工厂角色类 3 /// </summary> 4 public class SimpleFactory 5 { 6 public static Operation GetOperation(string operate) 7 { 8 Operation operation = null; 9 switch (operate) 10 { 11 case "+": 12 operation = new AddOperation(); 13 break; 14 case "-": 15 operation = new SubOperation(); 16 break; 17 case "*": 18 operation = new MulOperation(); 19 break; 20 case "/": 21 operation = new DivOperation(); 22 break; 23 } 24 25 return operation; 26 } 27 }
抽象产品: 定义产品行为(这里定义的是2个数及返回结果)
1 /// <summary> 2 /// 生产的产品基类:算法类(抽象产品角色) 3 /// 这里定义: 由 具体实现算法类计算得出最后结果 4 /// </summary> 5 public abstract class Operation 6 { 7 /// <summary> 8 /// 第一个数 9 /// </summary> 10 public double NumberA { get; set; } = 0.0000d; 11 12 public double NumberB { get; set; } = 1.00000d; 13 14 /// <summary> 15 /// 生产最后的计算结果 16 /// </summary> 17 /// <returns></returns> 18 public abstract double GetResult(); 19 }
具体产品: 本示例定义了加 减 乘 除4种具体产品实现类分别实现该抽象产品
1 /// <summary> 2 /// 具体的产品实现角色 :加法操作类 3 /// </summary> 4 public class AddOperation : Operation 5 { 6 /// <summary> 7 /// 具体实现产品角色:--->拿到其对应产品结果 8 /// </summary> 9 /// <returns></returns> 10 public override double GetResult() 11 { 12 return NumberA + NumberB; 13 } 14 } 15 16 /// <summary> 17 /// 具体的产品实现角色 : 减法操作类 18 /// </summary> 19 public class SubOperation : Operation 20 { 21 /// <summary> 22 /// 具体实现产品角色:--->拿到其对应产品结果 23 /// </summary> 24 /// <returns></returns> 25 public override double GetResult() 26 { 27 return NumberA - NumberB; 28 } 29 } 30 31 /// <summary> 32 /// 具体的产品实现角色 : 乘法操作类 33 /// </summary> 34 public class MulOperation : Operation 35 { 36 /// <summary> 37 /// 具体实现产品角色:--->拿到其对应产品结果 38 /// </summary> 39 /// <returns></returns> 40 public override double GetResult() 41 { 42 return NumberA * NumberB; 43 } 44 } 45 46 /// <summary> 47 /// 具体的产品实现角色 : 除法操作类 48 /// </summary> 49 public class DivOperation : Operation 50 { 51 /// <summary> 52 /// 具体实现产品角色:--->拿到其对应产品结果 53 /// </summary> 54 /// <returns></returns> 55 public override double GetResult() 56 { 57 if (0 == NumberB) 58 { 59 throw new DivideByZeroException(); 60 } 61 62 return NumberA / NumberB; 63 } 64 }
Client客户端: 根据工厂中动态实例化具体产品,执行该具体产品行为
1 /// <summary> 2 /// 简单工厂也叫静态工厂方法--从实际来讲并不是一种设计模式 3 /// 但我们将之列入进来与抽象工厂模式、工厂方法模式一起 4 /// 是为了剖析整个的设计过程的演进 5 /// Note : 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。 6 /// 简单工厂几大角色: 简单工厂、抽象产品、具体产品 7 /// </summary> 8 class Client 9 { 10 static void Main(string[] args) 11 { 12 var operate = "*"; 13 var operation = SimpleFactory.GetOperation(operate); 14 operation.NumberA = 2.50d; 15 operation.NumberB = 2.00d; 16 var result = operation.GetResult(); 17 18 Console.WriteLine("{0} {1} {2} = {3}", operation.NumberA, operate, operation.NumberB, result); 19 Console.ReadKey(); 20 } 21 }
假设我们目前先来计算 * 法,OK,我们传入 * 法即可
OK,假设第2道题要计算2个数的减法呢? easy,我们之需要将 * 改为 -即可
但因为目前我们只支持+ - * / 如果此时程序员说,我TM要的是求模呢? 好吧,我们目前确实不支持,如果要实现还必须使用简单工厂,so how to do?
如第2节所说,我们需要增加一个MolOperation去继承Operation, 同时在SimpleFactory的GetOperation方法的swtich判断块中加入一个case "%"的分支,确实如第2节所说,我们虽然扩展了产品实现方式(增加了求模产品实现,对扩展开放),但却破坏了“对修改关闭”,我们需要修改简单工厂部分的GetOperation方法,所以说简单工厂中封装了用户条件导致的判断逻辑,动态实现了具体产品的实例化,解析了客户端与具体产品之前的耦合。
2. 工厂方法
主要角色: 抽象工厂(或工厂接口)、具体工厂、抽象产品、具体产品、客户端
还是以之前计算2个数的结果的示例来看:
工厂接口:创建算法产品(这里使用基类,而非具体类),这里注意:工厂方法模式只会生产一种产品
1 /// <summary> 2 /// 工厂接口(也可以使用抽象工厂类) 3 /// </summary> 4 public interface IFactory 5 { 6 /// <summary> 7 /// 创建算法产品(这里使用基类,而非具体类) 8 /// 这里注意:工厂方法模式只会生产一种产品 9 /// </summary> 10 /// <returns></returns> 11 Operation CreateOperation(); 12 }
具体工厂:每个具体工厂负责对应具体产品的实例化
1 /// <summary> 2 /// 具体工厂角色类----加法工厂 3 /// </summary> 4 public class AddFactory : IFactory 5 { 6 /// <summary> 7 /// 加法工厂创建加法算法产品 8 /// Note : 工厂模式中:具体工厂只会生产对应具体产品 9 /// </summary> 10 /// <returns></returns> 11 public Operation CreateOperation() 12 { 13 return new AddOperation(); 14 } 15 } 16 17 /// <summary> 18 /// 具体工厂角色类----减法工厂 19 /// </summary> 20 public class SubFactory : IFactory 21 { 22 /// <summary> 23 /// 减法工厂创建减法算法产品 24 /// Note : 工厂模式中:具体工厂只会生产对应具体产品 25 /// </summary> 26 /// <returns></returns> 27 public Operation CreateOperation() 28 { 29 return new SubOperation(); 30 } 31 } 32 33 /// <summary> 34 /// 具体工厂角色类----乘法工厂 35 /// </summary> 36 public class MulFactory : IFactory 37 { 38 /// <summary> 39 /// 乘法工厂创建乘法算法产品 40 /// Note : 工厂模式中:具体工厂只会生产对应具体产品 41 /// </summary> 42 /// <returns></returns> 43 public Operation CreateOperation() 44 { 45 return new MulOperation(); 46 } 47 } 48 49 /// <summary> 50 /// 具体工厂角色类----除法工厂 51 /// </summary> 52 /// <returns></returns> 53 public class DivFactory : IFactory 54 { 55 /// <summary> 56 /// 除法工厂创建除法算法产品 57 /// Note : 工厂模式中:具体工厂只会生产对应具体产品 58 /// </summary> 59 /// <returns></returns> 60 public Operation CreateOperation() 61 { 62 return new DivOperation(); 63 } 64 }
抽象产品:与简单工厂中作用及源码一致
具体产品:与简单工厂中作用及源码一致
客户端:定义对应的创建对象的接口或抽象,然后在其子类(具体工厂)中去决定实例化那个具体产品。
1 /// <summary> 2 /// 工厂模式是由简单工厂演化而来,其目的是为了解决简单工厂破坏了软件设计开闭原则的 3 /// 定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类 4 /// 工厂方法模式:明显我们只是让客户端与工厂相互,而不是与产品关联。同时具体产品的产品是由具体工厂实例化出来的 5 /// 工厂方法模式几大角色: 抽象工厂(工厂接口)、具体工厂、抽象产品、具体产品、客户端 6 /// </summary> 7 class Client 8 { 9 static void Main(string[] args) 10 { 11 IFactory factory = new AddFactory(); 12 var operation = factory.CreateOperation(); 13 operation.NumberA = 2.500d; 14 operation.NumberB = 2.000d; 15 var result = operation.GetResult(); 16 Console.WriteLine("当前是加法"); 17 Console.WriteLine("输入2个数是:{0}及{1}结果是{2}", operation.NumberA, operation.NumberB, result); 18 19 Console.ReadKey(); 20 } 21 }
此时如果要变更为减法,只需要将接口或者抽象类的实现变更为减法工厂(对应的具体工厂)即可:IFactory factory = new SubFactory();
此时,我们注意到当去变更需要的产品时,我们只是在客户端改动了对应的具体实现工厂即可。如果再要实现对应的取模呢?
OK,我们需要创建取模工厂(MolFactory去实现IFactory),同时创建取模产品(MolOperation去继续Operation),然后将IFactory factory = new MolOperation()即可
现在再来看,工厂方法是不是只是扩张了取模工厂及对应产品,然后没有去修改其他工厂及产品,是不是现在已经符合了”开-闭原则“了吧。
然而,是否真的一切就完美了呢???
我们观察我们的工厂方法自始至终都是一个在生产一个产品在玩,假使现在我们的工厂要处理多种产品呢?
比如案例 : 福建某地某工厂以前给温州一公司代理做鞋,后来因温州公司破产而改接国外adidas品牌的鞋,OK,这个我们可以更改工厂为温州鞋厂为Adidas鞋厂搞定,very easy,后来经营一周后发现还可以代理adidas的衣服跟箱包,这时我们发现我们的工厂方法中工厂却不能再造东西了(因为我们只能生产一种产品), so how to do?
3. 抽象工厂模式
主要角色: 抽象工厂(或工厂接口)、具体工厂、抽象产品、具体产品、客户端
抽象工厂(或工厂接口): 定义一系列产品族的创建接口,创建抽象产品系列(产品族:多个产品)
1 /// <summary> 2 /// 抽象类角色 3 /// 创建抽象产品系列(产品族:多个产品) 4 /// </summary> 5 public abstract class AbstractFactory 6 { 7 /// <summary> 8 /// 创建产品族中A 9 /// </summary> 10 /// <returns></returns> 11 public abstract AbstractProductA CreateProductA(); 12 13 /// <summary> 14 /// 创建产品族中B 15 /// </summary> 16 /// <returns></returns> 17 public abstract AbstractProductB CreateProductB(); 18 }
具体工厂: 继承抽象工厂按抽象工厂中定义的产品族接口去创建具体的多个产品(产品族)
1 /// <summary> 2 /// 具体工厂1----生产对应产品A1及B1 3 /// </summary> 4 public class ConCreateFactory1 : AbstractFactory 5 { 6 public ConCreateFactory1() 7 { 8 Console.WriteLine("当前工厂使用ConCreateFactory1"); 9 } 10 11 public override AbstractProductA CreateProductA() 12 { 13 Console.WriteLine("ConCreateFactory1创建产品A1"); 14 15 return new ProductA1(); 16 } 17 18 public override AbstractProductB CreateProductB() 19 { 20 Console.WriteLine("ConCreateFactory1创建产品B1"); 21 22 return new ProductB1(); 23 } 24 } 25 26 /// <summary> 27 /// 具体工厂1----生产对应产品A2及B2 28 /// </summary> 29 public class ConCreateFactory2 : AbstractFactory 30 { 31 public ConCreateFactory2() 32 { 33 Console.WriteLine("当前工厂使用ConCreateFactory2"); 34 } 35 36 public override AbstractProductA CreateProductA() 37 { 38 Console.WriteLine("ConCreateFactory2创建产品A2"); 39 40 return new ProductA2(); 41 } 42 43 public override AbstractProductB CreateProductB() 44 { 45 Console.WriteLine("ConCreateFactory2创建产品B2"); 46 47 return new ProductB2(); 48 } 49 }
抽象产品 :(M个,有多少个产品需创建定义多少抽象产品类,M>1)
1 /// <summary> 2 /// 抽象产品A 3 /// </summary> 4 public abstract class AbstractProductA 5 { 6 } 7 8 /// <summary> 9 /// 抽象产品B 10 /// </summary> 11 public abstract class AbstractProductB 12 { 13 }
具体产品 : 每个抽象产品有N种实现(N即为具体工厂个数,因为每个工厂都必须创建M种产品(M >1)]
1 /// <summary> 2 /// 产品A的第一种具体实现 3 /// </summary> 4 public class ProductA1 : AbstractProductA 5 { 6 } 7 8 /// <summary> 9 /// 产品A的另一种具体实现 10 /// </summary> 11 public class ProductA2 : AbstractProductA 12 { 13 } 14 15 /// <summary> 16 /// 产品B的第一种具体实现 17 /// </summary> 18 public class ProductB1 : AbstractProductB 19 { 20 } 21 22 /// <summary> 23 /// 产品A的另一种具体实现 24 /// </summary> 25 public class ProductB2 : AbstractProductB 26 { 27 }
客户端 : 根据实际情况取对应实际抽象工厂的具体工厂,然后在该具体工厂去实例化具体的一系列产品族(这里将产品的实例化放至到最后)
1 /// <summary> 2 /// 抽象工厂模式:是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的 3 /// 注意:有N个产品族,在抽象工厂类中就应该有N个创建方法。 4 /// </summary> 5 class Client 6 { 7 static void Main(string[] args) 8 { 9 // 假设我们需要生产 产品A1 产品B1 则我们需要通过具体工厂ConCreateFactory1 10 AbstractFactory factory = new ConCreateFactory1(); 11 AbstractProductA productA = factory.CreateProductA(); 12 AbstractProductB productB = factory.CreateProductB(); 13 14 Console.WriteLine("---------------现在产品切换将由ConCreateFactory2工厂来生产---------------------"); 15 // 当切换成另一具体工厂ConCreateFactory2来实现时,则由ConCreateFactory2工厂通过拿到对应的产品具体 16 factory = new ConCreateFactory2(); 17 productA = factory.CreateProductA(); 18 productB = factory.CreateProductB(); 19 20 Console.ReadKey(); 21 } 22 }
分析:抽象工厂整个客户只与抽象工厂关联,与并不依赖于实际产品,而整个客户端其实依赖的变更只需要变更对应的具体工厂即可。整体符合软件设计的开-闭原则及依赖倒置原则。
说完3种工厂,其实简单来看,他们3个正是为了解决软件设计中的一些不太好的设计原则为不停优化改进的。
另外,其实对于我们示例中UI里涉及到的 AbstractFactory factory = new TFactory()或者IFactory factory = new TFactory(); 然后每次我们需要去变更对应的Factory实现时,又得更改代码AbstactFactory factory = new T2Factory()或者IFactory factory = new T2Factory(),改完又得重新编译,非常不方便,其实我们可以使用配置+反射的原理来实现该部分功能,通过反射+文件配置方式后,完全不必重新编译代码,只需要变更对应的xml配置即可。
最后本文demo代码下载地址:https://gitee.com/jimmyTown_admin_admin/FactoryPatternDemo