抽象工厂模式
1)问题
工厂方法模式中的每个工厂只生产一类产品,会导致系统中存在大量的工厂类,增加系统的开销。
2)概述
a)产品族 和 产品等级结构
产品等级结构:产品的继承结构;
产品族:由同一个工厂生产,位于不同产品等级结构中的一组产品;
b)适用场景
当系统所提供的工厂,生产的是多个位于不同产品等级结构、属于不同类型的具体产品时,可以使用抽象工厂模式。
工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构。
每个具体工厂可以生产属于一个产品族的所有产品,例如生产颜色相同的正方形、圆形和椭圆形,所生产的产品又位于不同的产品等级结构中。
如果使用工厂方法模式,需要提供15个具体工厂,而使用抽象工厂模式只需要提供5个具体工厂,极大减少了系统中类的个数。
c)定义
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;
在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族;
3)角色
AbstractFactory(抽象工厂):声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
ConcreteFactory(具体工厂):实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
AbstractProduct(抽象产品):为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
ConcreteProduct(具体产品):定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
abstract class AbstractFactory {
public abstract AbstractProductA createProductA(); //工厂方法一
public abstract AbstractProductB createProductB(); //工厂方法二
......
}
class ConcreteFactory1 extends AbstractFactory {
//工厂方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
//工厂方法二
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
......
}
与工厂方法模式一样,抽象工厂模式也可为每一种产品提供一组重载的工厂方法,以不同的方式对产品对象进行创建。
4)案例-初始方案
问题:
增加新皮肤时,需要增加大量类,每个新增具体组件都需要增加一个具体工厂,类的个数成对增加,影响系统维护成本和运行开销;
每个组件都需要一个具体工厂,在使用时要逐个设置,在客户端代码和配置文件都较为复杂。
5)案例-重构后
//按钮接口:抽象产品
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();
}
}
配置文件 config.xml 中存储了具体工厂类的类名
<?xml version="1.0"?>
<config>
<className>SpringSkinFactory</className>
</config>
客户端代码
class Client {
public static void main(String args[]) {
//使用抽象层定义
SkinFactory factory;
Button bt;
TextField tf;
ComboBox cb;
factory = (SkinFactory)XMLUtil.getBean();
bt = factory.createButton();
tf = factory.createTextField();
cb = factory.createComboBox();
bt.display();
tf.display();
cb.display();
}
}
如果需要增加新的皮肤,只需增加一族新的具体组件并提供一个新的具体工厂,修改配置文件即可使用新的皮肤,原有代码无须修改
6)开闭原则的倾斜性
在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦。
增加产品族:只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。
增加新的产品等级结构:需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法。
7)总结
1.优点
增加新的产品族很方便,无须修改已有系统。
2.缺点
增加新的产品等级结构需要对原有系统进行较大修改,需要修改抽象层代码。
3.适用场景
用户无须关心对象的创建过程,将对象的创建和使用解耦。
系统中有多于一个的产品族,而每次只使用其中某一产品族。
属于同一个产品族的产品将在一起使用。
产品等级结构稳定,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。