抽象工厂模式
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
——GoF
意即: 为创建一组相关或相互依赖的对象提供一个接口,而且无需制定它们的具体类
抽象工厂模式属于对象的创建型模式,又称为工具箱(Kit或ToolKit)模式。
不管是简单工厂模式,还是工厂方法模式,都有一个缺陷,那就是整个模式当中只能有一个抽象产品。工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品。即工厂方法模式具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但有时我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
产品等级结构和产品族
产品等级结构:即产品的继承结构,如一个抽象产品是手机,器具体产品就有华为手机、小米手机等,则抽象手机与具体品牌的手机之间构成了一个产品等级结构,抽象手机是父类,具体的品牌手机是其子类。
产品族:在抽象工厂模式中,产品族是指同一个工厂生产的,位于不同产品等级结构中的一组产品,如华为生产的有华为手机、华为笔记本、华为音像等。
其实,抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。抽象工厂模式是所有形式的工厂模式中最为抽象和最具有一般性的一种形态。
抽象工厂模式结构
抽象工厂模式的UML类图如下:
抽象工厂模式涉及以下四类角色:
- 抽象工厂:抽象工厂模式的核心,声明一个创建抽象产品对象的操作接口,通常使用接口或者抽象类实现
- 实体工厂:实现或继承抽象工厂类,实现创建具体产品对象的操作
- 抽象产品:抽象产品为一类产品对象声明一个接口
- 具体产品:一个将要被相应的具体工厂创建的产品对象
这四种角色的关系是:用抽象工厂生产抽象产品,用实体工厂生产实体产品,用抽象产品提供实体产品访问接口,用实体产品实现响应的功能
下面通过一个例子来说抽象工厂模式:
就笔记本生产商业务来说,之前使用了工厂方法模式模拟了笔记本电脑的生产。现OEM生产商决定除了生产笔记本电脑外,也开始生产手机。于是,OEM核心工厂决定改变现有策略,每个子厂生产同一品牌的两个产品,例如,子工厂A生产华为的手机和笔记本电脑,子工厂B生产小米的手机和笔记本电脑。这时,有了同族产品的概念,即某种产品是一个产品族的,这里就是指同一个品牌的。
UML类图设计如下:
此例中,手机和笔记本电脑属于不同产品等级,但是同一品牌的手机和电脑属于同一产品族
实例代码:
抽象产品类:MobilePhone.java 、Laptop .java
public abstract class MobilePhone {
public abstract void activate();
}
public abstract class Laptop {
private String brand;
public Laptop(String str) {
this.brand = str;
}
abstract public String getId();
public void getDescription() {
System.out.println("电脑品牌:" + this.brand);
}
}
具体产品类:MiPhone.java、HuaweiPhone.java、HuaweiLaptop.java、MiLaptop.java
public class MiPhone extends MobilePhone {
@Override
public void activate() {
System.out.println("手机品牌:Mi");
}
}
public class HuaweiPhone extends MobilePhone {
@Override
public void activate() {
System.out.println("手机品牌:HUAWEI");
}
}
public class HuaweiLaptop extends Laptop {
private final static String brand = "HUAWEI";
private static int id;
public HuaweiLaptop() {
super(brand);
id = 1000;
}
@Override
public String getId() {
return "序列号:" + id++;
}
}
public class MiLaptop extends Laptop {
private final static String brand = "Mi";
private static int id;
public MiLaptop() {
super(brand);
id = 2000;
}
@Override
public String getId() {
return "序列号:" + id++;
}
}
抽象工厂类:AbstractFactory.java
public interface AbstractFactory {
MobilePhone getMobilePhone();
Laptop getLaptop();
}
具体工厂类:HuaweiFactory.java、MiFactory.java
public class HuaweiFactory implements AbstractFactory {
@Override
public MobilePhone getMobilePhone() {
return new HuaweiPhone();
}
@Override
public Laptop getLaptop() {
return new HuaweiLaptop();
}
}
public class MiFactory implements AbstractFactory {
@Override
public MobilePhone getMobilePhone() {
return new MiPhone();
}
@Override
public Laptop getLaptop() {
return new MiLaptop();
}
}
测试:
public class FactoryTest {
public static void main(String[] args) {
AbstractFactory f1 = new HuaweiFactory();
Laptop p1 = f1.getLaptop();
p1.getDescription();
System.out.println(p1.getId());
MobilePhone p2 = f1.getMobilePhone();
p2.activate();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>");
AbstractFactory f2 = new MiFactory();
Laptop p3 = f2.getLaptop();
p3.getDescription();
System.out.println(p3.getId());
MobilePhone p4 = f2.getMobilePhone();
p4.activate();
}
}
测试结果:
抽象工厂模式的使用情况
抽象工厂模式是工厂方法模式的升级版本,当有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。具体而言,在以下情况下可以考虑使用抽象工厂模式:
第一,一个系统应当不依靠于产品类实例被创立组成和表示的细节。这对于所有形态的工厂模式都是重要的。用户无须关心对象的创建过程,将对象的创建和使用解耦。
第二,这个系统的产品有多于一个的产品族,而每次只使用其中某-一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
第三,同属于同一个产品族的产品是设计成在一起使用的。这一约束必须得 在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统 下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。
第四,不同的产品以一系 列的接口的面貌出现,从而使系统不依靠于接口实现的细节。其中第二第三个条件是我们选用抽象工厂模式而非其他形态的工厂模式的关键条件。
抽象工厂模式的优缺点
优点:
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
- 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。