设计模式(3)抽象工厂模式(Abstract Factory)
0 抽象工厂模式简介
0.0 抽象工厂模式定义
抽象工厂模式一般的书面定义为:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类
提供创建接口,无需指定具体实现类,这个原则在简单工厂模式和工厂方法模式篇已经反复讲到了,这是面向接口编程的一个基本准则,很显然,从抽象工厂模式的一般定义中可以看出这也是抽象工厂模式的一个核心,而抽象工厂模式的另一个核心是要解决一系列相关或相依赖对象(产品族)创建的问题。这也是这几种工厂模式的细节区别所在的关键点所在,初学设计模式时往往因为没有认识到这一点区别而对几种工厂模式难以理解
我们可以简单的这么理解,抽象工厂模式不单单要解决对象的创建问题,还要涉及到创建的对象之间的相互依赖或者约束关系。工厂方法模式和简单工厂模式解决了单个产品对象创建的问题,它们只关心单个产品对象的创建,而抽象工厂模式是关注的产品族(多个相互关联对象)的创建,最终创建的产品族形成一个新的产品对象。
抽象工厂模式的结构图如下:
AbstractFactory:抽象工厂类,定义创建某个产品所需相关对象的操作接口
ProductFactory1/ProductFactory2:具体的工厂类,实现抽象工厂定义的方法,负责某个产品所需所有对象创建操作接口的具体实现。
AbstractProductA/AbstractProductB:定义某一类产品对象的接口
ProductA1/ProductA2/ProductB1/ProductB2:具体产品对象的实现
0.1 抽象工厂模式应用场景
如果对上面的文字描述还不能有一个直观的认识,我们仍然从一个简单的应用场景来说明抽象工厂模式的引入时机。
野外商店老板为了更好的服务众英雄,特将店内隐身药水进行了一次升级,分为红瓶装(瓶口直径2cm)、蓝瓶装(瓶口直径5cm)2款,并且2种瓶子分别搭配圆形(2cm)和方形(5cm)两种款式的瓶盖。英雄们来到商店后,只需要将自己的的需要告诉商店老板,老板根据英雄的要求,去取瓶子和盖子进行组装即可。
在应用抽象工厂模式之前,我们很简单的使用简单工厂模式实现这个需求。
定义瓶子接口及实现类
/// <summary> /// 定义瓶子接口 /// </summary> public interface IBottle { /// <summary> /// 展示自己的信息 /// </summary> void ShowInfo(); }
/// <summary> /// 红色瓶子 /// </summary> public class RedBottle : IBottle { /// <summary> /// 展示自己的信息 /// </summary> public void ShowInfo() { Console.WriteLine("我是一个热情火辣的红色瓶子,我的瓶口直径是2cm。"); } }
定义瓶盖接口及实现类
/// <summary> /// 定义瓶盖接口 /// </summary> public interface ICap { /// <summary> /// 展示自己的信息 /// </summary> void ShowInfo(); }
/// <summary> /// 圆形瓶盖 /// </summary> public class RoundCap : ICap { /// <summary> /// 展示自己的信息 /// </summary> public void ShowInfo() { Console.WriteLine("我是一个圆形瓶盖,我的直径是2cm。"); } }
/// <summary> /// 方形瓶子 /// </summary> public class SquareCap : ICap { /// <summary> /// 展示自己的信息 /// </summary> public void ShowInfo() { Console.WriteLine("我是一个方形瓶盖,我的直径是5cm。"); } }
定义创建瓶子和瓶盖的简单工厂
/// <summary> /// 瓶子创建工厂方法 /// </summary> public class BottleFactory { /// <summary> /// 创建瓶子对象 /// </summary> /// <param name="color">瓶子颜色</param> /// <returns></returns> public static IBottle CreateBottle(string color) { if (color == "red") return new RedBottle(); if (color == "blue") return new BlueBottle(); return null; } }
/// <summary> /// 瓶盖创建工厂方法 /// </summary> public class CapFactory { /// <summary> /// 创建瓶子对象 /// </summary> /// <param name="shape">瓶盖形状</param> /// <returns></returns> public static ICap CreateCap(string shape) { if (shape == "round") return new RoundCap(); if (shape == "square") return new SquareCap(); return null; } }
最终成品组装类定义
/// <summary> /// 隐形药水组装者 /// </summary> public class ProductMaker { private IBottle _bottle; // 瓶子对象 private ICap _cap; // 瓶盖对象 /// <summary> /// 创建最终药水对象 /// </summary> /// <param name="bottleColor"></param> /// <param name="capShape"></param> public void MakeProduct(string bottleColor, string capShape) { _bottle = BottleFactory.CreateBottle(bottleColor); _cap = CapFactory.CreateCap(capShape); Console.WriteLine("准备英雄需要的瓶子和瓶盖。"); _bottle.ShowInfo(); _cap.ShowInfo(); Console.WriteLine("开始往瓶子了灌入隐形药水,然后封上瓶盖。"); } }
客户端调用
static void Main(string[] args) { ProductMaker pm = new ProductMaker(); pm.MakeProduct("red", "round"); // 提供给英雄红瓶子圆盖子的隐形药水 Console.WriteLine(); pm.MakeProduct("blue", "square"); // 提供给英雄蓝瓶子方盖子的隐形药水 Console.ReadLine(); }
这样,我们通过接口进行隔离,解决了不同瓶子,不同盖子创建时,客户端无需关心具体创建过程,只需要告诉工厂需要何种类型的对象即可。但同时会暴露一个很明显的问题就是,我们创建的瓶子和瓶盖2个对象并不是孤立存在的,2个对象之间是有明显的相互关系的,那就是平口尺寸必须和瓶盖尺寸保持一直,否则就会让店老板出现灌装好药水却拧不上盖子的尴尬。此时应该会对上面提到的抽象工厂的定义有进一步的理解,也在这种场景下,是我们必须要引入抽象工厂模式的时候了。
1 抽象工厂模式详解
0、提炼抽象工厂类
根据抽象工厂方法的结构图,我们首先定义个一个抽象工厂类,该抽象工厂定义瓶子和瓶盖2个对象的创建接口。
/// <summary> /// 抽象工厂类 /// </summary> public abstract class AbstractFactory { /// <summary> /// 创建瓶子 /// </summary> /// <returns></returns> public abstract IBottle CreateBottle(); /// <summary> /// 创建瓶盖 /// </summary> /// <returns></returns> public abstract ICap CreateCap(); }
1、抽象工厂的具体实现
分析产品的组合情况,实际上只存在2种类型的最终产品,红瓶子圆盖子和蓝瓶子方盖子,我们分别定义2个抽象工厂的具体实现类
/// <summary> /// 红瓶子圆盖子工厂类 /// </summary> public class RedBottleAndRoundCapFactory : AbstractFactory { /// <summary> /// 创建瓶子 /// </summary> /// <returns></returns> public override IBottle CreateBottle() { return BottleFactory.CreateBottle("red"); } /// <summary> /// 创建瓶盖 /// </summary> /// <returns></returns> public override ICap CreateCap() { return CapFactory.CreateCap("round"); } }
/// <summary> /// 蓝瓶子方盖子工厂类 /// </summary> public class BlueBottleAndSquareCapFactory : AbstractFactory { /// <summary> /// 创建瓶子 /// </summary> /// <returns></returns> public override IBottle CreateBottle() { return BottleFactory.CreateBottle("blue"); } /// <summary> /// 创建瓶盖 /// </summary> /// <returns></returns> public override ICap CreateCap() { return CapFactory.CreateCap("square"); } }
2、最终产品组装类的修改实现
跟原来的实现相比较,最终产品组装类不在从客户端传入瓶子、瓶盖的参数通过对应的工厂方法创建,而是直接传入已经定义好的瓶子瓶盖组装工厂类对象,能够做到瓶子瓶盖必须配套创建。
/// <summary> /// 创建最终药水对象 /// </summary> /// <param name="factory">抽象工厂具体对象</param> public void MakeProduct(AbstractFactory factory) { _bottle = factory.CreateBottle(); _cap = factory.CreateCap(); Console.WriteLine("准备英雄需要的瓶子和瓶盖。"); _bottle.ShowInfo(); _cap.ShowInfo(); Console.WriteLine("开始往瓶子了灌入隐形药水,然后封上瓶盖。"); }
3、客户端调用
ProductMaker pm = new ProductMaker(); AbstractFactory factory = new RedBottleAndRoundCapFactory(); // 提供给英雄红瓶子圆盖子的隐形药水 pm.MakeProduct(factory); Console.WriteLine(); factory = new BlueBottleAndSquareCapFactory(); // 提供给英雄蓝瓶子方盖子的隐形药水 pm.MakeProduct(factory); Console.ReadLine();
2 总结
通过创建的多个对象之间的关联关系阐述了抽象工厂模式与其他工厂模式的区别以及使用时机。基于上面的示例,我们可以总结出抽象工厂模式的具有以下优点:
0、分离接口和实现
客户端使用抽象工厂方法来创建需要的对象,只需要传入抽象方法的,无需关注内部具体实现逻辑,实现接口与具体实现的分离解耦。
1、易于产品族切换
一个具体抽象工厂的实现其实就是代表一个产品族,客户端通过参数选用不同的工厂实现,就可以在不同的产品创建中进行切换。
抽象工厂模式的缺点也显而易见,那就是扩展新产品的过程会比较麻烦,比如一个产品族中包含的产品发生了变化,比如增加或减少部件,就需要修改抽象工厂,同时需要修改所有的抽象工厂实现类。