一、工厂方法模式
模式动机与定义
- 工厂方法模式(Factory Method Pattern)简称工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。
- 在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类
模式结构
模式特点
- 工厂方法模式是简单工厂模式的进一步抽象和推广
- 工厂方法模式保持了简单工厂模式的优点,并克服了它的缺点
- 核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给其子类去完成
- 可以允许系统在不修改工厂角色的情况下引进新产品
- 增加具体产品–>增加具体工厂,符合“开闭原则”
模式实例
将原有的电视机工厂进行分割,为每种品牌的电视机提供一个子工厂,海尔工厂专门负责生产海尔电视机,海信工厂专门负责生产海信电视机,如果需要生产TCL电视机或创维电视机,只需要对应增加一个新的TCL工厂或创维工厂即可,原有的工厂无须做任何修改,使得整个系统具有更加的灵活性和可扩展性。
类图:
当需要增加一个具体电视机类时(如LGTV),只需要对应添加一个对应的具体工厂即可(如LGTVFactory),这样就不会像简单工厂模式那样违背开闭原则了。
作业例子及编程实现
在某网络管理软件中,需要为不同的网络协议提供不同的连接类,例如针对POP3协议的连接类POP3Connection、针对IMAP协议的连接类IMAPConnection、针对HTTP协议的连接类HTTP Connection等。由于网络连接对象的创建过程较为复杂,需要将其创建过程封装到专门的类中,该软件还将支持更多类型的网络协议,现采用工厂方法模式进行设计,绘制类图并编程模拟实现。
设计类图:
为了使创建不同网络连接工厂时不用修改到源代码,代码实现使用了Java的反射机制来创建对应的工厂类。
网络连接接口类NetworkConnection:
public interface NetworkConnection {
}
三个网络连接具体类:
public class HTTPConnection implements NetworkConnection{
}
public class IMAPConnection implements NetworkConnection{
}
public class POP3Connection implements NetworkConnection{
}
抽象工厂类NetworkConnectionFactory:
public interface NetworkConnectionFactory {
NetworkConnection createConnection();
}
分别用于创建三个网络连接的具体工厂类:
public class HTTPConnectionFactory implements NetworkConnectionFactory {
@Override
public NetworkConnection createConnection() {
System.out.println("创建HTTP连接");
return new HTTPConnection();
}
}
public class IMAPConnectionFactory implements NetworkConnectionFactory {
@Override
public NetworkConnection createConnection() {
System.out.println("创建IMAP连接");
return new IMAPConnection();
}
}
public class POP3ConnetionFactory implements NetworkConnectionFactory {
@Override
public NetworkConnection createConnection() {
System.out.println("创建POP3连接");
return new POP3Connection();
}
}
解析xml文件类MyXMLUtil类:
public class MyXMLUtil {
static public String getTagValue(String tagName){
File file =
new File("xml/creatorPatternHomework/network_connection_factory.xml");
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc = builder.parse(file);
NodeList nl = doc.getElementsByTagName(tagName);
Node node = nl.item(0).getFirstChild();
return node.getNodeValue().trim();
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
主要的功能是接受一个xml文件中的标签名,返回第一个该名字标签中的值。
保存具体工厂名的xml文件:
客户端类Client类:
public class Client {
public static void main(String[] args) throws Exception {
// 读取xml文件中保存的具体工厂类名
String className = MyXMLUtil.getTagValue("className");
// 通过工厂类名创建对应的工厂(反射机制)
Class<?> factoryClass = Class.forName(className);
NetworkConnectionFactory factory = (NetworkConnectionFactory)factoryClass.newInstance();
// 使用工厂创建具体网络连接对象
NetworkConnection networkConnection = factory.createConnection();
}
}
运行结果:
当我们修改xml文件保存的类名后:
1.改为生产HTTP连接的工厂类
2.改为生产POP3连接的工厂类
模式优缺点
优点:
- 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
- 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
- 在系统中加入新产品时,完全符合开闭原则
缺点: - 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
- 增加了系统的抽象性和理解难度
- 一般情况依赖反射机制,像c++这种原生不支持反射的语言难以使用
二、抽象工厂模式
模式动机与定义
- 产品等级结构:产品等级结构即产品的继承结构,例如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
- 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,例如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。(可以理解为一个系列的东西,如一套套装,一套智能家居等)
抽象工厂模式定义: - 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
- 抽象工厂模式又称为Kit模式,属于对象创建型模式。
模式结构
模式实例
各个操作系统的GUI控件系列:
具体例子和编码实现
一个电器工厂可以产生多种类型的电器,如海尔工厂可以生产海尔电视机、海尔空调等,TCL工厂可以生产TCL电视机、TCL空调等,相同品牌的电器构成一个产品族,而相同类型的电器构成了一个产品等级结构,现使用抽象工厂模式模拟该场景。
类图:
空调接口:
public interface AirConditioner {
void changeTemperature();
}
两个具体空调类:
public class HaierAirConditioner implements AirConditioner {
@Override
public void changeTemperature() {
System.out.println("海尔空调制冷");
}
@Override
public String toString() {
return "海尔空调";
}
}
public class HisenseAirConditioner implements AirConditioner {
@Override
public void changeTemperature() {
System.out.println("海信空调制冷");
}
@Override
public String toString() {
return "海信空调";
}
}
电视接口:
public interface TV {
void play();
}
两种具体电视:
public class HaierTV implements TV {
@Override
public void play() {
System.out.println("海尔电视播放");
}
@Override
public String toString() {
return "海尔电视";
}
}
public class HisenseTV implements TV {
@Override
public void play() {
System.out.println("海信电视播放");
}
@Override
public String toString() {
return "海信电视";
}
}
抽象工厂:
public interface EFactory {
TV produceTelevision();
AirConditioner produceAirConditioner();
}
两个具体工厂:
public class HaierFactory implements EFactory {
@Override
public TV produceTelevision() {
return new HaierTV();
}
@Override
public AirConditioner produceAirConditioner() {
return new HaierAirConditioner();
}
}
public class HisenseFactory implements EFactory {
@Override
public TV produceTelevision() {
return new HisenseTV();
}
@Override
public AirConditioner produceAirConditioner() {
return new HisenseAirConditioner();
}
}
客户端类:
public class Client {
public static void main(String[] args) throws Exception {
// 获取具体工厂类名(写在xml文件中)
String className = MyXMLUtil.getTagValue("xml/creatorPatternHomework/ebrand.xml", "className");
// 根据类名(String)使用反射创建工厂对象
EFactory eFactory = (EFactory)(Class.forName(className).newInstance());
// 使用工厂生产 tv和air-conditioner
TV tv = eFactory.produceTelevision();
AirConditioner airConditioner = eFactory.produceAirConditioner();
System.out.println(tv);
tv.play();
System.out.println(airConditioner);
airConditioner.changeTemperature();
}
}
xml文件:
运行结果:
修改为Haier工厂:
运行结果:
当我们需要再添加一个TCL系列的产品时(即产品族),只需要:
①编写对应的各个具体产品类
②编写对应的具体工厂
这样就可以轻松完成需求。
但如果我们需要添加一个产品等级,即这里添加一个新产品空调,则我们需要修改抽象工厂类,添加一个produceAirConditioner()函数,然后得修改各个类的实现,违反了开闭原则。
模式优缺点
优点:
- 隔离了具体类的生成,使得客户端并不需要知道什么被创建
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
- 增加新的产品族很方便,无须修改已有系统,符合开闭原则
缺点:
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则
模式适用情况
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节
- 系统中有多于一个的产品族,但每次只使用其中某一产品族
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
- 产品等级结构稳定,在设计完成之后不会向系统中增加新的产品等级结构或者删除已有的产品等级结构