抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。 |
抽象工厂模式结构
工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。
在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法具有唯一性,一般情况下,一个具体工厂中只有一个或者一组重载的工厂方法。但是有时候我们希望一个工厂可以提供多个产品对象,而不是单一的产品对象。为了更好地理解抽象工厂模式,我们先引入两个概念:
(1) 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
(2) 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。
当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。
在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构类图如下所示:
![](https://img-blog.csdn.net/20170419163337878?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjc4NTM4Mg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在抽象工厂模式结构图中包含如下几个角色:
● AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
● ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
● AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
● ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
抽象工厂模式实现
解决方案一 抽象工厂模式的实现 在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类。具体工厂实现了抽象工厂,每一个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族。其关键实现代码如下所示: public abstract class AbstractFactory {
public abstract AbstractProductA createProductA(); // 工厂方法一
public abstract AbstractProductB createProductB(); // 工厂方法二
}
public class ConcreteFactory1 extends AbstractFactory {
// 工厂方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
// 工厂方法二
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory {
// 工厂方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
// 工厂方法二
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
}
public abstract class AbstractProductA{
public void sameOperation();
}
public class ConcreteProductA1 extends AbstractProductA{
public void sameOperation(){
}
}
public class ConcreteProductA2 extends AbstractProductA{
public void sameOperation(){
}
}
public abstract class AbstractProductB{
public void sameOperation();
}
public class ConcreteProductB1 extends AbstractProductA{
public void sameOperation(){
}
}
public class ConcreteProductB2 extends AbstractProductA{
public void sameOperation(){
}
}
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
public class XMLUtil {
// 该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean() {
try {
// 创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));
// 获取包含类名的文本节点
NodeList nodeList = doc.getElementsByTagName("className");
Node classNode = nodeList.item(0).getFirstChild();
String className = classNode.getNodeValue();
// 通过类名生成实例对象并将其返回
Class class = Class.forName(className);
Object object = class.newInstance();
return object;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
<?xml version="1.0"?>
<config>
<className>ConcreteFactory1</className>
</config>
public class Client {
public static void main(String args[]) {
// 使用抽象层定义
AbstractFactory factory;
AbstractProductA abstractProductA;
AbstractProductB abstractProductB;
factory = (AbstractFactory)XMLUtil.getBean();
abstractProductA = factory.createProductA();
abstractProductB = factory.createProductB();
abstractProductA.sameOperation();
abstractProductB.sameOperation();
}
}
|
开闭原则的倾斜性
在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为“开闭原则”的倾斜性。这也是抽象工厂模式最大的缺点。“开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的,对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:
(1) 增加产品族:对于增加新的产品族,抽象工厂模式很好地支持了“开闭原则”,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。
(2) 增加新的产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了“开闭原则”。
正因为抽象工厂模式存在“开闭原则”的倾斜性,它以一种倾斜的方式来满足“开闭原则”,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。
适应场景
在以下情况下可以考虑使用抽象工厂模式:
(1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
(2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
(3) 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。
(4) 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
效果
抽象工厂模式是工厂方法模式的进一步延伸,由于它提供了功能更为强大的工厂类并且具备较好的可扩展性,在软件开发中得以广泛应用,尤其是在一些框架和API类库的设计中,例如在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。抽象工厂模式也是在软件开发中最常用的设计模式之一。
1. 主要优点
抽象工厂模式的主要优点如下:
(1) 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
(2) 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
(3) 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
2. 主要缺点
抽象工厂模式的主要缺点如下:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
应用实例
设计问题:COS系统是一款网上订餐软件,在COS系统中,CDrawer类负责绘制统计图表,图表包含两种类型的图形元素:饼状图(Pie)和条形图(Bar),图形预定义样式有:水晶样式(Crystal)和扁平样式(Flat),未来还可能增加3D图形样式,如何解决该设计问题? 解决方案:现用抽象工厂模式解决这个问题,在COS系统中,Pie和Bar充当抽象产品类,具体产品类有:FlatPie、FlatBar、CrystalPie、CrystalBar,ChartFactory充当抽象工厂类,具体工厂类有:FlatFactory、CrystalFactory,其类结构图如下所示: ![](https://img-blog.csdn.net/20170419173115094?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjc4NTM4Mg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 关键代码实现如下所示; public abstract class ChartFactory {
public abstract Pie createPie(); // 工厂方法一
public abstract Bar createBar(); // 工厂方法二
}
public class FlatStyleFactory extends ChartFactory {
// 工厂方法一
public Pie createPie() {
return new FlatPie();
}
// 工厂方法二
public Bar createBar() {
return new FlatBar();
}
}
public class CrystalStyleFactory extends ChartFactory {
// 工厂方法一
public Pie createPie() {
return new CrystalPie();
}
// 工厂方法二
public Bar createBar() {
return new CrystalBar();
}
}
public abstract class Pie{
public void display();
}
public class FlatPie extends Pie{
public void display(){
}
}
public class CrystalPie extends Pie{
public void display(){
}
}
public abstract class Bar{
public void display();
}
public class FlatBar extends Bar{
public void display(){
}
}
public class CrystalBar extends Bar{
public void display(){
}
}
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
public class XMLUtil {
// 该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean() {
try {
// 创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));
// 获取包含类名的文本节点
NodeList nodeList = doc.getElementsByTagName("className");
Node classNode = nodeList.item(0).getFirstChild();
String className = classNode.getNodeValue();
// 通过类名生成实例对象并将其返回
Class class = Class.forName(className);
Object object = class.newInstance();
return object;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
<?xml version="1.0"?>
<config>
<className>FlatCrystalFactory</className>
</config>
public class CDrawer {
public static void main(String args[]) {
// 使用抽象层定义
ChartFactory factory;
Pie pie;
Bar bar;
factory = (ChartFactory)XMLUtil.getBean();
pie = factory.createPie();
bar = factory.createBar();
pie.display();
bar.display();
}
}
|
参考书籍:http://www.chinasa.info/
code download:https://github.com/JyNeo/Software-Design-Pattern/blob/master/DesignPattern_CoreCode/AbstractFactoryPattern.java