抽象工厂模式
简介
抽象工厂模式(Abstract Factory Pattern )也是创建型模式之一,为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定他们的具体类
工厂方法模式和抽象工厂模式的区别
抽象工厂模式与工厂方法模式最大的区别就在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
产品等级结构与产品族
为了便于理解上面的概念,我们需要知道什么是产品等级结构和产品族
产品等级结构
产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。如下图为一个产品等级结构方式:
产品族
在抽象工厂模式中,产品族是位于不同产品等级结构中,功能相关联的产品组成的家族。如下图所示多个产品结构用红箭头标出来的:
每一个产品族中含有产品的数目与产品等级结构的数目是相等的
产品的等级结构与产品族将产品按照不同方向划分,形成一个二维的坐标系,横轴表示产品的等级结构,纵轴表示产品族。
只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
使用场景
- 一个系统不应当依赖于产品实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的
- 这个系统的产品有多于一个的产品族,而系统只消费某一族的产品
- 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖与实现
"系统只消费某一族的产品" 怎么理解,实际上是与抽象工厂模式的起源有关,具体可以参见《Java与模式》 抽象工厂的起源
UML类图
角色简介
- factory:抽象工厂角色,它声明了一组用于创建一种产品的方法,每一种方法对应一种产品,如上图的factory就定义了两个方法,分别创建ProductA和ProductB
- ConcreteFactory:具体工厂角色,它实现了再抽象工厂中定义的创建产品的方法,生成一组具体产品,这些产品构成了一个产品种类,每一个产品都位于某个产品等级结构中,如上图所述的ConcreteFactory1和ConcreteFactory2
- Product:抽象产品角色,它为每种产品声明接口,比如上述类图中的ProductA和ProductB
- ConcreteProduct:具体产品角色,它定义具体工厂生产的具体产品,实现抽象产品接口中声明的业务和方法,如上图中ConcreteProductA1、ConcreteProductA2和ConcreteProductB1、ConcreteProductB2
示例
这里我们以奥迪汽车的生产举例,奥迪Q3、Q5、Q7虽然都是一个车系,但是他们三者的零部件差别却很大,就拿Q3和Q7来说,Q3的发动机是国产的,Q7的则是原装进口的,Q3的轮胎是普通的轮胎,而Q7则使用的是全尺寸的越野轮胎;还有Q3使用的是普通的制动系统,Q7则使用制动性能极好的制动系统。Q3和Q7对应的是一些列车,而发动机、轮胎、制动系统则对应的是一系列零部件,两者是两种不同的产品类型,这时候就可以将抽象工厂模式应用到其中,首先汽车工厂需要生产轮胎、发动机、制动系统这3个部分。
首选我们先定义一个抽象车厂类
public interface CarFactory2 { /** * 生产轮胎 * @return 轮胎 */ ITire createTire(); /** * 生产引擎 * @return 引擎 */ IEngine createEngine(); /** * 生产制动系统 * @return 制动系统 */ IBrake createBrake();}
其次我们定义零部件抽象类,并实现Q3和Q7所使用的零部件
public interface IBrake { /** * 制动系统 */ void brake();}
public interface IEngine { /** * 发动机 */ void engine();}
public interface ITire { /** * 轮胎 */ void tire();}
实现Q3的零部件
public class NormalBrake implements IBrake { public void brake() { System.out.println("普通制动"); }}
public class DomesticEngine implements IEngine { public void engine() { System.out.println("国产发动机"); }}
public class NormalTire implements ITire { public void tire() { System.out.println("普通轮胎"); }}
实现Q7的零部件
public class SeniorBrake implements IBrake { public void brake() { System.out.println("高级制动"); }}
public class ImportEngine implements IEngine { public void engine() { System.out.println("进口发动机"); }}
public class SUVTire implements ITire { public void tire() { System.out.println("越野轮胎"); }}
最后我们进行测试
/** * 工厂模式测试类 */public class FactoryTest { public static void main(String[] args) { // Q3工厂 Q3CarFactory2 q3CarFactory2 = new Q3CarFactory2(); q3CarFactory2.createBrake().brake(); q3CarFactory2.createEngine().engine(); q3CarFactory2.createTire().tire(); System.out.println("=========================Q3、Q7分隔符================================"); // Q7 工厂 Q7CarFactory2 q7CarFactory2 = new Q7CarFactory2(); q7CarFactory2.createBrake().brake(); q3CarFactory2.createEngine().engine(); q7CarFactory2.createTire().tire(); }}
打印结果
普通制动
国产发动机 普通轮胎 =========================Q3、Q7分隔符================================ 高级制动 国产发动机 越野轮胎优点
一个显著的优点是分离接口与实现,客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已,使其从具体的产品实现中解耦
同时基于接口与实现的分离,使抽象该工厂方法模式在切换产品类时更加灵活、容易,无需修改已有的系统,符合”开闭-原则“。
缺点
由上面的示例我们可以看出,抽象工厂模式会导致类文件的爆炸性增加,例如上面我们要加入Q5工厂,对应的轮胎,引擎、和制动类又要增加
还有就是不太容易扩展新的产品类,因为每当我们增加一个产品类就需要修改抽象工厂,那么所有的具体工厂类均会被修改。(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)
参考资料
Java与模式 - 书
Android 源码设计模式解析与实战 - 书