意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
别名:
Kit
动机:
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
如何应对这种变化呢?如何绕开常规的对象创建方法(new),提供一种“封装机制”来避免客户端程序和这种“多系列具体对象创建工作”的高耦合?
1.一个系统需要消费多个抽象产品角色,这些抽象产品角色可以用java接口或者抽象类来实现,既然客户端需要这些抽象产品角色的实例,为什么不使用一个工厂类负责创建这些角色的实例?工厂类负责创建抽象产品的实例描述如下图所示。
但是,如上所说,这些抽象产品角色都是有java接口或者抽象类实现的,而Java接口或者抽象类是不能实例化的,所以,上面的设计是不成立的。
2.怎样满足系统需求?
根据里式代换原则,任何接收父类型的地方,都应当能够接收子类型。因此,实际上,系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体实例如下图所示。
3.如果每个抽象产品都有多于一个的具体子类的话,工厂角色怎么知道实例化哪个子类呢?比如下图中就给出了两个抽象产品,而每一个抽象产品都有两个具体产品。
抽象工厂模式提供两个具体工厂角色,分别对应于这两个具体产品角色,每一个具体工厂角色仅负责某一个具体产品角色的实例化。每一个具体工厂类负责创建抽象产品的某一个具体子类的实例。如下图
适用性
- 同一个产品族的产品在一起使用时,而且它们之间是相互依赖里的,不可分离。
- 系统需要由相互关联的多个对象来构成。
- 当先提供一组对象而不显示它们的实现过程,只显示它们的接口。
- 系统不应当依赖某一些具体产品类。
相关概念
产品族:
在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。
例如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族
产品等级结构:
产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
抽象工厂模式的定义
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式,抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定具体产品的情况下,创建多个产品族中的产品对象。
“抽象”来自抽象产品角色,而“抽象工厂”就是抽象产品角色的工厂。
抽象工厂模式的特点
- 隔离了具体类的生成,客户不需要知道怎样生成了每一个具体产品和什么时间生成的。它将客户与具体的类分离,依赖于抽象类,耦合度低。
- 一个产品族中的多个对象被设计成一起工作,它能够保证客户端始终只使用一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是非常实用的。
- 它有利于更新产品系统,由于客户端只依赖于抽象类,更新产品系列时,只须更新具体工厂名就行了。
- 难以支持新种类的产品,因为抽象工厂接口确定了可以被创建的产品集合,支持新种类的产品就需要扩展该工厂接口,这将引起抽象工厂类及其所有子类的改变。
结构与参与者
参与者:AbstractFactory 声明一个创建抽象产品对象的接口
ConcreteFactory 实现创建具体产品对象的操作
AbstractProduct 为一类产品对象声明一个接口
ProductXX 定义被相应具体工厂创建的产品对象
Client 仅使用AbstractFactory和AbstractProduct类声明的接口
示例代码:
public abstract class AbstractProductA {
public abstract void show();
}
public abstract class AbstractProductB {
public abstract void show();
}
public class ProductA1 extends AbstractProductA {
@Override
public void show() {
System.out.println("我是产品A1");
}
}
public class ProductB1 extends AbstractProductB{
@Override
public void show() {
System.out.println("我是产品B1");
}
}
public class ProductA2 extends AbstractProductA {
@Override
public void show() {
System.out.println("我是产品A2");
}
}
public class ProductB2 extends AbstractProductB {
@Override
public void show() {
System.out.println("我是产品B2");
}
}
public class ConcreteFactory1 extends AbstractFactory{
@Override
public AbstractProductA CreateProductA() {
return new ProductA1();
}
@Override
public AbstractProductB CreateProductB() {
return new ProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory{
@Override
public AbstractProductA CreateProductA() {
return new ProductA2();
}
@Override
public AbstractProductB CreateProductB() {
return new ProductB2();
}
}
public abstract class AbstractFactory {
public static AbstractFactory CreateFactory(String type){
AbstractFactory abstractFactory=null;
switch (type){
case "1":
abstractFactory=new ConcreteFactory1();
break;
case "2":
abstractFactory=new ConcreteFactory2();
break;
}
return abstractFactory;
}
//生产A类产品
public abstract AbstractProductA CreateProductA();
//生产B类产品
public abstract AbstractProductB CreateProductB();
}
public class Client {
public static void main(String[] args) {
AbstractFactory abstractFactory=AbstractFactory.CreateFactory("1");
abstractFactory.CreateProductA().show();
abstractFactory.CreateProductB().show();
//修改
abstractFactory=AbstractFactory.CreateFactory("2");
abstractFactory.CreateProductA().show();
abstractFactory.CreateProductB().show();
}
}
运行结果:
我是产品A1
我是产品B1
我是产品A2
我是产品B2
效果:
- 分离接口和实现
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。 - 使切换和添加产品族变得容易
因为一个具体的工厂实现代表的是一个产品族,添加一个新的具体工厂类是很方便的。
示例代码:
public interface Phone {
String getProductInfo();
}
public interface Computer {
String getProductInfo();
}
public class ApplePhone implements Phone{
@Override
public String getProductInfo() {
return "苹果手机,采用ios系统";
}
}
public class SamsungPhone implements Phone{
@Override
public String getProductInfo() {
return "三星手机,采用android系统";
}
}
public class AppleComputer implements Computer {
@Override
public String getProductInfo() {
return "苹果电脑,采用mac系统";
}
}
public class SamsungComputer implements Computer {
@Override
public String getProductInfo() {
return "三星电脑,采用windows系统";
}
}
public interface ElectronicFactory {
Phone producePhone();
Computer produceComputer();
}
public class AppleFactory implements ElectronicFactory {
@Override
public Phone producePhone() {
return new ApplePhone();
}
@Override
public Computer produceComputer() {
return new AppleComputer();
}
}
public class SamsungFactory implements ElectronicFactory {
@Override
public Phone producePhone() {
return new SamsungPhone();
}
@Override
public Computer produceComputer() {
return new SamsungComputer();
}
}
public class Client {
public static void main(String[] args) {
ElectronicFactory appleFactory=new AppleFactory();
Phone applePhone=appleFactory.producePhone();
System.out.println(applePhone.getProductInfo());
Computer appleComputer =appleFactory.produceComputer();
System.out.println(appleComputer.getProductInfo());
ElectronicFactory samsungFactory=new SamsungFactory();
Phone sumsungPhone=samsungFactory.producePhone();
System.out.println(sumsungPhone.getProductInfo());
Computer sumsungComputer =samsungFactory.produceComputer();
System.out.println(sumsungComputer.getProductInfo());
}
}
运行结果:
苹果手机,采用ios系统
苹果电脑,采用mac系统
三星手机,采用android系统
三星电脑,采用windows系统
抽象工厂模式优点:
抽象工厂模式隔离了具体类的生成,用户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的。
抽象工厂模式缺点:
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)
“开闭原则”的倾斜性
“开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级机构的系统,其功能增强包括两方面:
1.增加产品族:对于增加新的产品族,工厂方法模式很好的支持了“开闭原则”,对于新增加的产品族,只需要对应增加一个新的具体工厂即可,对已有代码无需做任何修改。
2.增加新的产品等级结构对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,没有很好支持“开闭原则”。
已知应用
1.JAVA SE AWT
在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。
2.在很多软件系统中需要更换界面主题,要求界面中的按钮,文本框,背景色等一起发生改变时。可以使用抽象工厂模式进行设计。
工厂模式的退化:
当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式成工厂模式;当工厂模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂模式就退化成简单工厂模式。
相关模式:
AbstractFactory类通常用工厂方法实现,但它们也可以用Prototype(原型)实现,它们也可以一起使用,AbstractFactory可以储存一个被克隆的原型的集合,并返回产品对象。
一个具体的工厂通常是一个单件(Singleton)。