标签:修改 隔离 机构 扩展 选择 es2017 通过 良好的 圆形
要理解抽象工厂模式,首先要了解几个概念,一个是产品等级结构,另一个是产品族。
在工厂方法模式中引入了工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个具体工厂只有一个或者一组重载的工厂方法,只能生产一种产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销,有时候可能需要一个工厂能够提供多种产品对象,而不是单一的产品对象,例如海尔电器公司可以生产海尔电视、海尔冰箱、海尔洗衣机等,公司或工厂可以生产多种产品,而不是单一的某个产品。此时,可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是本章将要学习的抽象工厂模式的基本思想。为了更好的理解抽象工厂模式,先引入以下两个概念,就是产品等级结构和产品族。
产品等级结构:产品等级结构就是产品的继承结构,例如一个抽象类是电视机,其子类可以是海尔电视、海信电视、小米电视,则抽象电视机与具体品牌的电视机之间构成了一个产品的等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。例如海尔电视、海尔冰箱和海尔洗衣机是同一个产品族,这个产品族由三个分属不同的产品等级结构中,组合成了一个产品族。
产品等级结构和产品族的示意图如下:
在上图,不同颜色的多个正方形、圆形和椭圆形分别构成了3个不同的产品等级结构,而颜色相同的正方形,圆形和椭圆构成了一个产品族,每一个形状对象都位于某个产品族,并属于某个产品等级结构,上图共有5个产品族,分属于3个产品等级结构中。只要指定一个产品所处的产品族和他所属的等级结构,就可以确定这个唯一的产品。
当系统所提供的工厂生产的具体产品并不是一个简单的额对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。抽象工厂模式时多有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对单一的产品结构,而抽象工厂模式需要面对多个产品等级结构。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。抽象工厂模式的示意图如下:
上图中,每一个具体工厂都可以生产一个产品族上面的所有产品,例如颜色相同的正方形、圆形、椭圆。这些产品分属于三个不同的产品等级结构。如果要用简单工厂模式来做,只设计一个工厂类,在其中要写15个创建不同产品的方法,职责过重;如果要用工厂方法模式来写,要分别写15个不同的实体工厂以及三个不同的抽象工厂(工厂方法模式是针对单一产品等级结构,所以要写三个不同的抽象工厂代表每一个产品等级结构),系统中的类的个数成倍增加,系统开销和复杂度都由上升;目前为止最好的办法就是使用抽象工厂模式了,只需要创建一个针对不同产品等级结构组成的产品族的一个抽象的工厂类,以及五个分别生产不同产品族的实体工厂。可以看出抽象工厂大大的减少了系统中类的数量。下面列出上面提到的关于抽象工厂模式中的各个组成部分:
①AbstractFactory(抽象工厂):它定义了一组创建一族产品的方法,每一个方法对应一种产品。
②ConcreteFactory(具体工厂):它继承或实现了抽象工厂,将抽象工厂定义的方法实现为自己特有的行为来生成特定的一族产品类。
③AbstractProduct(抽象产品):它为每种产品声明接口,声明了每种产品做具有的业务方法。
④ConcreteProduct(具体产品):定义具体的产品对象,实现或继承抽象产品中所定义的业务方法。
下图为抽象工厂模式的类图:
典型的抽象工厂的代码
abstract class AbstractFactory { public abstract AbstractProductA CreateProductA(); //工厂方法一 public abstract AbstractProductB CreateProductB(); //工厂方法二 …… }
典型的具体工厂的代码
class ConcreteFactory1 : AbstractFactory { //工厂方法一 public override AbstractProductA CreateProductA() { return new ConcreteProductA1(); } //工厂方法二 public override AbstractProductB CreateProductB() { return new ConcreteProductB1(); } …… }
某软件公司要开发一套界面皮肤库,可以对基于.NET平台的桌面软件进行界面美化。用户在使用时可以通过菜单来选择皮肤,不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等界面元素,例如春天(Spring)风格的皮肤将提供浅绿色的按钮、绿色边框的文本框和绿色边框的组合框,而夏天(Summer)风格的皮肤则提供浅蓝色的按钮、蓝色边框的文本框和蓝色边框的组合框,其结构示意图如下图所示:
该皮肤库需要具备良好的灵活性和可扩展性,用户可以自由选择不同的皮肤,开发人员可以在不修改既有代码的基础上增加新的皮肤。
现使用抽象工厂模式来设计该界面皮肤库。
通过分析,本实例的机构图如下:
上图中,SkinFactory充当抽象接口,其子类SpingSkinFactory和SummerSkinFactory充当具体工厂,接口Button、TextField、ComboBox充当抽象产品,其子类SpringButtong、SpingTextField等充当具体产品。
①Button:按钮接口,充当抽象产品
interface Button { void Display(); }
②SpringButton:SpringButton按钮类,充当具体产品
class SpringButton : Button { public void Display() { Console.WriteLine("显示浅绿色按钮。"); } }
③SummerButton:充当具体产品
class SummerButton : Button { public void Display() { Console.WriteLine("显示浅蓝色按钮。"); } }
④TextField:文本框接口,充当抽象产品
interface TextField { void Display(); }
⑤SpringTextField:具体产品
class SpringTextField : TextField { public void Display() { Console.WriteLine("显示绿色边框文本框。"); } }
⑥SummerTextField:具体类
class SummerTextField : TextField { public void Display() { Console.WriteLine("显示蓝色边框文本框。"); } }
⑦ComboBox:抽象产品
interface ComboBox { void Display(); }
⑧SpringComboBox:具体产品
class SpringComboBox : ComboBox { public void Display() { Console.WriteLine("显示绿色边框组合框。"); } }
⑨SummerComboBox:具体产品
class SummerComboBox : ComboBox { public void Display() { Console.WriteLine("显示蓝色边框组合框。"); } }
⑩SkinFactory:界面皮肤工厂接口,充当抽象工厂
interface SkinFactory { Button CreateButton(); TextField CreateTextField(); ComboBox CreateComboBox(); }
(11)SpringSkinFactory:具体的皮肤工厂
class SpringSkinFactory : SkinFactory { public Button CreateButton() { return new SpringButton(); } public TextField CreateTextField() { return new SpringTextField(); } public ComboBox CreateComboBox() { return new SpringComboBox(); } }
(12)SummerSkinFactory:具体的皮肤工厂
class SummerSkinFactory : SkinFactory { public Button CreateButton() { return new SummerButton(); } public TextField CreateTextField() { return new SummerTextField(); } public ComboBox CreateComboBox() { return new SummerComboBox(); } }
(13)客户端调用:
static void Main(string[] args) { //使用抽象层定义 SkinFactory factory; Button bt; TextField tf; ComboBox cb; //读取配置文件 string factoryType = ConfigurationManager.AppSettings["factory"]; //反射生成对象 factory = (SkinFactory)Assembly.Load("AbstractFactorySample").CreateInstance(factoryType); bt = factory.CreateButton(); tf = factory.CreateTextField(); cb = factory.CreateComboBox(); bt.Display(); tf.Display(); cb.Display(); Console.Read(); }
客户端调用还是使用配置文件和反射来做一些符合开闭原则的收尾工作。在这个系统中,如果要更换皮肤界面,只需要修改配置文件就可以了。
上面实例其实存在一个很严重的问题,那就是如果在设计之初忽略了一些细节,在后面要进行扩展时会非常困难,比如我们现在要增加一个单选按钮框,由于在抽象工厂类中已经将规则写死,要想增加单选按钮框,如果在工厂方法模式中的话直接新增抽象产品类+具体产品类+抽象工厂+具体工厂来对系统进行扩展即可,符合开闭原则,而在抽象工厂模式中是不能这样做的,因为抽象工厂类中的逻辑已经写死,在这个例子中,抽象工厂类只定义了Button CreateButton();TextField CreateTextField();ComboBox CreateComboBox();这三个方法,如果新增单选按钮,那么必须对源码进行修改,违反了开闭原则。
在抽象工厂模式中,新增产品族很方便,直接新增一个具体工厂和一族具体产品即可,但是如果新增产品等级结构,则会很麻烦,需要对源码进行修改,这样就违反了开闭原则。
抽象工厂模式是工厂方法模式的进一步的延伸,由于它提供了功能更为强大的工厂类并具备较好的可扩展性,在软件开发中广泛的得到了应用,尤其在一些框架和类库中。它是最常用的设计模式之一。
①隔离了具体类的生成,使得客户端并不需要知道什么被创建
②当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
③增加新的产品族很方便,无须修改已有系统,符合开闭原则
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则
①一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节
②系统中有多于一个的产品族,但每次只使用其中某一产品族
③属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
④产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构
标签:修改 隔离 机构 扩展 选择 es2017 通过 良好的 圆形
原文地址:http://www.cnblogs.com/pangjianxin/p/7986176.html