c#中工厂模式详解
一、总体介绍:
工厂模式主要有三种类型:简单工厂、工厂方法和抽象工厂,该模式用于封装和管理对象的创建,是一种创建型模式。
万物皆对象,创建对象时必然需要new该对象,当需要更改对象时,需要把项目中所有地方都修改一遍,这显然违背了软件设计的开闭原则。
如果使用工厂来生成对象,那么我们只需要跟工厂打交道就可以了。如果要更新对象时,直接在工厂里更换即可。这就实现了对象解耦。
所以工厂模式主要用来解耦代码,将对象的创建和使用分离,使得代码更加灵活和可维护。
定义创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
适用于创建对象需要大量重复的步骤,或者需要依赖于其它对象的情况,它提供了一种方式来封装多个相关或依赖对象的创建逻辑。
二、简单工厂基础介绍:
这是工厂模式的最基本形式,通过定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法模式,它属于类创建型模式,但不属于GOF23种设计模式。
简单工厂包含三大角色:
- 抽象产品(抽象类):定义了产品的规范,描述了产品的主要特征和功能。它是工厂类创建的所有对象的父类,封装了各种产品对象的共有方法。
- 具体产品(子类):继承抽象产品的子类,某个产品的具体实现类。
- 具体工厂(实例化对象类):它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;其可以被外界直接调用,创建所需的产品对象。
- 特性和功能:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
- 使用环境:当一个类不知道它所必须创建的对象的类的时候。
- 注意事项:每增加一个产品就需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度。
- 优点:一个调用者想创建一个对象,只要知道其名称就可以了。
- 缺点:增加新的产品需要修改工厂接口,违背了“开放-封闭原则”。
三、简单工厂创建方式:
- 首先定义一个抽象产品类。
1 /// <summary> 2 /// 该类是产品的父类即抽象产品,定义所有子类的共有属性和方法 3 /// </summary> 4 public abstract class Coffee 5 { 6 /// <summary> 7 /// 方便演示,只定义两个代表性方法。 8 /// </summary> 9 public abstract void GetName(); 10 11 public void AddSugar() 12 { 13 Console.WriteLine("加糖"); 14 } 15 }
该类主要定义了产品的共有属性和方法,用于子类继承和实现。
- 其次定义每个产品的具体实现子类。
1 public class AmericanCoffee : Coffee 2 { 3 public override void GetName() 4 { 5 Console.WriteLine("我是一杯美式咖啡。"); 6 } 7 } 8 9 public class LatterCoffe: Coffee 10 { 11 public override void GetName() 12 { 13 Console.WriteLine("我是一杯拿铁咖啡。"); 14 } 15 }
该类实现并继承自抽象类,如需增加产品可直接创建新的子类并继承自抽象类即可。
- 然后定义核心类,即工厂类。
1 public class CoffeeFactory 2 { 3 public CoffeeFactory() 4 { 5 } 6 7 /// <summary> 8 /// 简单工厂中必须要有一个方法来根据指定的逻辑创建实例 9 /// </summary> 10 /// <param name="fruitType"></param> 11 /// <returns></returns> 12 public static Coffee OrderCoffe(CoffeeEnum coffeeEnum) 13 { 14 switch (coffeeEnum) 15 { 16 case CoffeeEnum.AmericanCoffee: 17 return new AmericanCoffee(); 18 case CoffeeEnum.LatterCoffe: 19 return new LatterCoffe(); 20 } 21 return null; 22 } 23 24 public enum CoffeeEnum 25 { 26 AmericanCoffee, 27 LatterCoffe 28 } 29 }
该类通过创建了一个枚举类型参数来选择需要创建的产品实例。
- 最后客户端调用。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //通过CoffeeFactory产品工厂创建了AmericanCoffee产品实例 6 Coffee coffee = CoffeeFactory.OrderCoffe(CoffeeFactory.CoffeeEnum.AmericanCoffee); 7 coffee.GetName(); 8 9 //通过CoffeeFactory产品工厂创建了LatterCoffe产品实例 10 coffee = CoffeeFactory.OrderCoffe(CoffeeFactory.CoffeeEnum.LatterCoffe); 11 coffee.GetName(); 12 coffee.AddSugar(); 13 } 14 }
根据客户端的选择条件来动态的实例化相关的类,
对于客户端来说,其去除了与具体产品之间的依赖。
简单工厂模式的缺点主要就是违背了开-闭原则,
在上面的 Demo 中,如果我要再增加一种产品。
那么,首先是定义一个新产品子类,让其继承自抽象类,然后呢,您还必须修改工厂类。
所以进而改进形成了工厂方法模式。
四、工厂方法基础介绍:
这是一种更高级的工厂模式,它通过抽象接口或基类中的工厂方法来创建对象。
具体实现由子类负责,因此更加灵活。这种设计方式有利于实现开闭原则,即对扩展开放,对修改封闭。
简单工厂把全部的事情,在一个地方(类)全部处理完,而工厂方法却不同,
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
这样一来,扩展产品种类就不必修改工厂函数了,核心类就变成抽象类,工厂方法模式将生成具体产品的任务分发给具体的产品工厂。
也就是相当于工厂总部不生产产品了,交给下辖分工厂进行生产。
要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。
工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
工厂方法模式的主要角色:
抽象工厂:在抽象工厂类中声明了工厂方法,用于返回一个产品。提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
具体产品工厂:它是抽象工厂类的子类,实现了在抽象工厂中声明的工厂方法,完成具体产品的创建。并可由客户端调用,返回一个具体产品类的实例。
抽象产品:它是定义产品的接口,定义了产品的规范,描述了产品的主要特性和功能,是工厂方法模式所创建对象的公共父类。
具体产品:它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
缺点:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
五、工厂方法创建方式:
- 抽象产品类:
1 /// <summary> 2 /// 该类是产品的父类即抽象产品,定义所有子类的共有属性和方法 3 /// </summary> 4 public abstract class Coffee 5 { 6 /// <summary> 7 /// 方便演示,只定义两个代表性方法。 8 /// </summary> 9 public abstract void GetName(); 10 11 public void AddSugar() 12 { 13 Console.WriteLine("加糖"); 14 } 15 }
- 具体产品类:
1 public class AmericanCoffee : Coffee 2 { 3 public override void GetName() 4 { 5 Console.WriteLine("我是一杯美式咖啡。"); 6 } 7 } 8 9 public class LatterCoffe: Coffee 10 { 11 public override void GetName() 12 { 13 Console.WriteLine("我是一杯拿铁咖啡。"); 14 } 15 }
- 抽象工厂:
1 /// <summary> 2 /// 抽象工厂 3 /// </summary> 4 public abstract class CoffeeFactory 5 { 6 public abstract Coffee GetCoffee(); 7 }
- 具体产品工厂:
1 /// <summary> 2 /// 美式咖啡工厂 3 /// </summary> 4 public class AmericanFactory:CoffeeFactory 5 { 6 public override Coffee GetCoffee() 7 { 8 return new AmericanCoffee(); 9 } 10 } 11 12 /// <summary> 13 /// 拿铁咖啡工厂 14 /// </summary> 15 public class LatterFactory : CoffeeFactory 16 { 17 public override Coffee GetCoffee() 18 { 19 return new LatterCoffe(); 20 } 21 }
- 客户端调用:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //首先创建一个美式咖啡工厂,只负责生产美式咖啡产品 6 CoffeeFactory coffeeFactory = new AmericanFactory(); 7 //在美式咖啡工厂中生产一个美式咖啡产品 8 Coffee coffee = coffeeFactory.GetCoffee(); 9 coffee.GetName(); 10 11 //创建一个拿铁咖啡工厂,只负责生产拿铁咖啡产品 12 coffeeFactory = new LatterFactory(); 13 //在工厂中生产一个拿铁咖啡产品 14 coffee = coffeeFactory.GetCoffee(); 15 coffee.GetName(); 16 //咖啡中加糖 17 coffee.AddSugar(); 18 } 19 }
通过客户端调用方式可以看出,不同产品的生产由原来的总工厂变为了各个分工厂去负责。
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则。
六、抽象工厂基础介绍:
上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品,直接生成实例,这些工厂只生产同种类产品。
但是抽象工厂模式不同,抽象工厂模式并不直接生成实例, 而是用于对产品类簇的创建。
通俗点来讲就是:简单工厂和工厂方法模式的工作是生产产品,那么抽象工厂模式的工作就是生产工厂的。
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
抽象工厂的最大好处在于交换产品系列非常方便,只需要改变具体工厂即可使用不同的产品配置。
抽象工厂模式的主要角色:
抽象工厂:在抽象工厂类中声明了多个工厂方法,用于返回多个产品。提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
具体产品工厂:它是抽象工厂类的子类,实现了在抽象工厂中声明的多个工厂方法,完成多个具体产品的创建。
抽象产品:它是定义一个产品的接口,定义了一个产品的规范,描述了一个产品的主要特性和功能。抽象工厂模式有多个抽象产品。
具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
总的来说工厂方法模式一个工厂只生产一个产品,抽象工厂模式一个工厂生产多个产品,形成一个产品套餐,而多个工厂组成套餐系列。
七、抽象工厂创建方式:
- 抽象产品类,有多少个不同产品就创建多少个抽象产品类。
1 /// <summary> 2 /// 咖啡产品抽象类 3 /// </summary> 4 public abstract class Coffee 5 { 6 /// <summary> 7 /// 方便演示,只定义两个代表性方法。 8 /// </summary> 9 public abstract void GetName(); 10 11 public void AddSugar() 12 { 13 Console.WriteLine("加糖"); 14 } 15 } 16 17 /// <summary> 18 /// 甜点产品抽象类 19 /// </summary> 20 public abstract class Dessert 21 { 22 public abstract void GetName(); 23 }
实例中创建了两种产品,即咖啡和甜品。
- 具体产品类,不同产品继承不同抽象类。
1 public class AmericanCoffee : Coffee 2 { 3 public override void GetName() 4 { 5 Console.WriteLine("我是一杯美式咖啡。"); 6 } 7 } 8 9 public class LatterCoffe: Coffee 10 { 11 public override void GetName() 12 { 13 Console.WriteLine("我是一杯拿铁咖啡。"); 14 } 15 } 16 17 public class MatchaMousse: Dessert 18 { 19 public override void GetName() 20 { 21 Console.WriteLine("我是一块抹茶慕斯。"); 22 } 23 } 24 25 public class Tiramisu: Dessert 26 { 27 public override void GetName() 28 { 29 Console.WriteLine("我是一块提拉米苏。"); 30 } 31 }
实例中定义了两种咖啡和两种甜点,咖啡为一个产品等级,甜点是另外一个产品等级。
- 抽象工厂类,有多少个系列就可以创建多少个抽象工厂类。本实例只创建了一个风味系列的工厂。
1 /// <summary> 2 /// 风味工厂 3 /// </summary> 4 public abstract class RelishFactory 5 { 6 //生产一杯咖啡 7 public abstract Coffee GetCoffee(); 8 //生产一块甜点 9 public abstract Dessert GetDessert(); 10 }
实例中定义了一个系列的工厂,如果新增一个不同产品等级的奶茶,那就可以在风味工厂中生产一杯奶茶。同一种口味绑定为一个系列即一个抽象工厂。
- 具体工厂类,同一系列可以创建多个具体的工厂,负责同一系列下的不同产品出的创建。本实例创建了风味系列下的不同风味工厂的创建。
1 /// <summary> 2 /// 美式风味工厂 3 /// </summary> 4 public class AmericanRelishFactory : RelishFactory 5 { 6 public override Coffee GetCoffee() 7 { 8 return new AmericanCoffee(); 9 } 10 11 public override Dessert GetDessert() 12 { 13 return new MatchaMousse(); 14 } 15 } 16 17 /// <summary> 18 /// 意大利风味工厂 19 /// </summary> 20 public class ItalyRelishFactory : RelishFactory 21 { 22 public override Coffee GetCoffee() 23 { 24 return new LatterCoffe(); 25 } 26 27 public override Dessert GetDessert() 28 { 29 return new Tiramisu(); 30 } 31 }
实例中创建了两种不同口味的工厂,选择对应口味的工厂,可以生产出对应口味的不同产品。
比如选择了美式口味工厂,该工厂可以生产出美式咖啡和抹茶慕斯。
实际上是该工厂将这两个不同等级的产品进行绑定,形成了一个产品族。
- 客户端调用:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //首先创建一种口味工厂 6 RelishFactory relishFactory = new AmericanRelishFactory(); 7 //然后对应口味工厂中生产出对应口味的不同产品。 8 Coffee coffee = relishFactory.GetCoffee(); 9 coffee.GetName(); 10 Dessert dessert = relishFactory.GetDessert(); 11 dessert.GetName(); 12 13 Console.WriteLine("换一种口味"); 14 relishFactory = new ItalyRelishFactory(); 15 coffee = relishFactory.GetCoffee(); 16 coffee.GetName(); 17 dessert = relishFactory.GetDessert(); 18 dessert.GetName(); 19 } 20 }
实例中可以看出创建了一个美式口味工厂,该工厂就可以生产出符合该口味的不同产品。
其本质就是将相同口味的不同产品绑定成一个产品族,形成一个产品族工厂。
如果有多个产品族,那就创建多个产品族工厂就可以了。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
八、总结:
简单工厂把全部的事情,在一个地方(类)全部处理完,
而工厂方法却不同,
其是通过创建一个框架,
然后让子类决定要如何实现。
而抽象工厂则是定义一个负责创建一组产品(也就是一个产品族)的接口,
抽象工厂的最大好处在于交换产品系列非常方便。
上述简单工厂模式和工厂方法模式都是直接生成实例,但是抽象工厂模式不同,抽象工厂模式并不直接生成实例, 而是用于对产品类族的创建。
通俗点来讲就是:简单工厂和工厂方法模式的工作是生产产品,那么抽象工厂模式的工作就是生产工厂的(产品类族)。
从上面可看到,简单工厂的优点就是我们只要传递正确的参数,就能获得所需的对象,而不需要关心其创建的具体细节。
工厂方法的优点就是让其子类决定具体实现,如日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等。
抽象工厂模式一般用于严格要求以面向对象思想进行开发的超大型项目中。