一、抽象工程模式的内容
抽象工厂模式又称为Kit模式,属于对象创建型模式。它提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。
二、抽象工厂模式的动机
在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
为了更清晰地理解工厂方法模式,需要先引入两个概念:
产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
![](https://img-my.csdn.net/uploads/201206/08/1339158063_7117.png)
当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。 对于工厂模式和抽象工厂模式不同可以理解为:工厂模式只能生产一种产品(不管如何变化,如上篇工厂模式中不同风味的披萨),而抽象工厂模式可以生产多个产品。
抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。 对于工厂模式和抽象工厂模式不同可以理解为:工厂模式只能生产一种产品(不管如何变化,如上篇工厂模式中不同风味的披萨),而抽象工厂模式可以生产多个产品。
![](https://img-my.csdn.net/uploads/201206/08/1339158185_6881.png)
三、抽象工厂模式结构图
![](https://img-my.csdn.net/uploads/201206/08/1339158429_1456.gif)
抽象工厂(Abstract Factory)角色:担任这个角色的是工厂方法模式的核心,它是与应用系统商业逻辑无关的。
具体工厂(Concrete Factory)角色:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。
抽象产品(Abstract Product)角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。
具体产品(Concrete Product)角色:抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。
四、三种工厂模式的要点
- 所有的工厂都是用来封装对象的创建。
- 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
- 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。(待商榷)
- 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中。
- 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
- 工厂方法允许类将实例化延迟到子类进行。
- 抽象工厂创建相关的对象实现簇,而不需要依赖他们的具体类。
- 依赖倒置原则,指导我们避免依赖具体类型,而要尽量依赖抽象。
- 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。
五、抽象工厂模式示例代码
抽象工厂
public interface EFactory
{
public Television produceTelevision();//生产电视
public AirConditioner produceAirConditioner();//生产空调
}
具体工厂
public class HaierFactory implements EFactory
{
public Television produceTelevision()
{
return new HaierTelevision();
}
public AirConditioner produceAirConditioner()
{
return new HairAirConditioner();
}
}
public class TCLFactory implements EFactory
{
public Television produceTelevision()
{
return new TCLTelevision();
}
public AirConditioner produceAirConditioner()
{
return new TCLAirConditioner();
}
}
抽象产品
public interface Television
{
public void play();
}
public interface AirConditioner
{
public void changeTemperature();
}
具体产品
public class HaierTelevision implements Television
{
public void play()
{
System.out.println("海尔电视机播放中......");
}
}
public class HairAirConditioner implements AirConditioner
{
public void changeTemperature()
{
System.out.println("海尔空调温度改变中......");
}
public class TCLTelevision implements Television
{
public void play()
{
System.out.println("TCL电视机播放中......");
}
}
public class TCLAirConditioner implements AirConditioner
{
public void changeTemperature()
{
System.out.println("TCL空调温度改变中......");
}
}
XML配置文件
<?xml version="1.0"?>
<config>
<className>HaierFactory</className>
</config>
读取XML工具
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 nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}
测试类
public class Client
{
public static void main(String args[])
{
try
{
EFactory factory;
Television tv;
AirConditioner ac;
factory=(EFactory)XMLUtil.getBean();
tv=factory.produceTelevision();
tv.play();
ac=factory.produceAirConditioner();
ac.changeTemperature();
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
}
六、抽象工厂模式优缺点
抽象工厂模式的优点
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)
七、抽象工厂模式的使用环境
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
八、抽象工厂模式在JDK当中的应用
Java SE AWT(抽象窗口工具包)
在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。
在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。
在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时,可以使用抽象工厂模式进行设计。
Abstract factory(recognizeable by creational methods returning an abstract/interface type)
-
java.util.Calendar#getInstance()
-
java.util.Arrays#asList()
-
java.util.ResourceBundle#getBundle()
-
java.net.URL#openConnection()
-
java.sql.DriverManager#getConnection()
-
java.sql.Connection#createStatement()
-
java.sql.Statement#executeQuery()
-
java.text.NumberFormat#getInstance()
-
java.lang.management.ManagementFactory
(allgetXXX()
methods) -
java.nio.charset.Charset#forName()
-
javax.xml.parsers.DocumentBuilderFactory#newInstance()
-
javax.xml.transform.TransformerFactory#newInstance()
-
javax.xml.xpath.XPathFactory#newInstance()
-
java.net.URLStreamHandlerFactory#createURLStreamHandler(String)
(Returns singleton object per protocol)
九、参考文献
- 《Head First 设计模式》
- 《设计模式》刘伟主编清华大学出版社
- http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns