工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由 于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必 会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂 来统一生产,这就是我们本文将要学习的抽象工厂模式的基本思想。
ps:重点:产品族的概念 ;
先上定义:
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而 无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。
这个是不是有点儿乱 我们看下它的结构图先:
分析一波:
1.客户端需要 产品AbstractProductA 和AbstractProductB 所以他要找一个能生产AbstractProductA 和AbstractProductB 的工厂 Factory (不知道是哪个工厂);
2.后来发现有两个工厂能生产这两个产品:ConcreteFactory1 和 ConcreteFactory2;
3.这两个工厂对A、B两种产品有着不同的实现工艺:
ConcreteFactory1 :ConcreteProductA1、ConcreteProductB1
ConcreteFactory2 :ConcreteProductA2、ConcreteProductB2
在抽象工厂模式结构图中包含如下几个角色:
● AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一 种产品。
● ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具 体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
● AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的 业务方法。
● ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中 声明的业务方法。
在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可 以是抽象类或者具体类
看明白了他的套路 我们来举个栗子:
Sunny软件公司欲开发一套界面皮肤库,
可以对Java桌面软件进行界面美化。
为了保护版权, 该皮肤库源代码不打算公开,而只向用户提供已打包为jar文件的class字节码文件。
用户在使 用时可以通过菜单来选择皮肤,不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等 界面元素,
该皮肤库需要具备良好的灵活性和可扩展性,用户可以自由选择不同的皮肤,开发人员可以 在不修改既有代码的基础上增加新的皮肤。
需求图:
看看用抽象工厂模式我们怎么解决它:
UML结构图:
上代码:
//在本实例中我们对代码进行了大量简化,实际使用时,界面组件的初始化代码较为复杂,还需要使用JD //按钮接口:抽象产品 interface Button { public void display(); } //Spring按钮类:具体产品 class SpringButton implements Button { public void display() { System.out.println("显示浅绿色按钮。"); } } //Summer按钮类:具体产品 class SummerButton implements Button { public void display() { System.out.println("显示浅蓝色按钮。"); } } //文本框接口:抽象产品 interface TextField { public void display(); } //Spring文本框类:具体产品 class SpringTextField implements TextField { public void display() { System.out.println("显示绿色边框文本框。"); } } //Summer文本框类:具体产品 class SummerTextField implements TextField { public void display() { System.out.println("显示蓝色边框文本框。"); } } //组合框接口:抽象产品 interface ComboBox { public void display(); } //Spring组合框类:具体产品 class SpringComboBox implements ComboBox { public void display() { System.out.println("显示绿色边框组合框。"); } } //Summer组合框类:具体产品 class SummerComboBox implements ComboBox { public void display() { System.out.println("显示蓝色边框组合框。"); } } //界面皮肤工厂接口:抽象工厂 interface SkinFactory { public Button createButton(); public TextField createTextField(); public ComboBox createComboBox(); } //Spring皮肤工厂:具体工厂 class SpringSkinFactory implements SkinFactory { public Button createButton() { return new SpringButton(); } public TextField createTextField() { return new SpringTextField(); } public ComboBox createComboBox() { return new SpringComboBox(); } } //Summer皮肤工厂:具体工厂 class SummerSkinFactory implements SkinFactory { public Button createButton() { return new SummerButton(); } public TextField createTextField() { return new SummerTextField(); } public ComboBox createComboBox() { return new SummerComboBox(); } } class Client { public static void main(String args[]) { //使用抽象层定义 SkinFactory factory; Button bt; TextField tf; ComboBox cb; factory = new SpringSkinFactory(); bt = factory.createButton(); tf = factory.createTextField(); cb = factory.createComboBox(); bt.display(); tf.display(); cb.display(); } } } |
看完代码我们发现 切换皮肤很方便 开发新皮肤也很方便 。
但是如果我们现在想给一个新的元素加皮肤,比如单选按钮。我们会发现 每一种皮肤都要维护自己的皮肤工厂,来实现单选渲染逻辑。这违背了开闭原则。我们的新需求改动了几乎所有的抽象工厂代码。
怎么办? 没法办,这也是抽象工厂最大的缺点。
在抽象工 厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这 种性质称为“开闭原则”的倾斜性。也就是说需要设计之初考虑全面,减少增加新的产品结构的操作。
抽象工厂方法总结:
抽象工厂模式的主要优点如下:
(1) 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离, 更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接 口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
(2) 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产 品族中的对象。
(3) 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
1. 主要缺点 抽象工厂模式的主要缺点如下:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码, 这显然会带来较大的不便,违背了“开闭原则”。
1. 适用场景 在以下情况下可以考虑使用抽象工厂模式:
(1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工 厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
(2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来 使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
(3) 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个 产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作 系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统 的,此时具有一个共同的约束条件:操作系统的类型。
(4) 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的 产品等级结构。