适用环境:一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。用户无须关心对象的创建过程,将对象的创建和使用解耦。系统有多于一个产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。属于同一个产品族的产品将一起使用,这一约束必须在系统的设计中体现出来。
应用:很多系统软件中需要更换界面主题,要求界面的按钮/文本框/背景颜色发生改变时。在Java中的AWT(抽象窗口工具包)中就使用了抽象工厂模式,使得在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。
产品等级结构:海尔电视机、海信电视机
产品族:海尔电视机、海尔冰箱
抽象工厂模式是工厂方法模式的泛化版,工厂方法模式是一种特殊的抽象工厂模式。
当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品是需要使用抽象工厂模式。
抽象工厂模式和工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式 。
模式结构图:
代码:
客户端:
package com.abstractfactory.hing;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
OSFactory win = (OSFactory)XMLUtil.getBean();
Button wb = win.produceButton();
wb.show();
OSFactory lin = new LinuxFactory();
Form f = lin.produceForm();
f.show();
}
}
抽象产品类
package com.abstractfactory.hing;
public interface Button {
public void show();
}
具体产品类:
package com.abstractfactory.hing;
public class WindowsButton implements Button {
public void show() {
System.out.println("Windows 按钮就是好看!");
}
}
抽象工厂类:
package com.abstractfactory.hing;
public interface OSFactory {
public Button produceButton();
public Form produceForm();
}
具体工厂类:
package com.abstractfactory.hing;
public class WindowsFactory implements OSFactory{
@Override
public Button produceButton() {
return new WindowsButton();
}
@Override
public Form produceForm() {
return new WindowsForm();
}
}
工具类:
package com.abstractfactory.hing;
import java.io.File;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class XMLUtil {
public static Object getBean() {
try {
//创建DOM文档对象
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dfactory.newDocumentBuilder();
Document doc = builder.parse(new File("config.xml"));
//获取结点
NodeList nl =doc.getElementsByTagName("className");
Node n = nl.item(0).getFirstChild();
String cName = n.getNodeValue();
System.out.println(cName);
Class c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
}catch(Exception e) {
e.printStackTrace();
}
return null;
}
}
config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<confg>
<className>com.abstractfactory.hing.LinuxFactory</className>
</confg>
该模式的难点在于对抽象工厂和抽象产品之间关系的理解,其中两个概念:产品等级结构,产品族的理解也是关键。
优点:
①隔离了具体类的生成,使得客户端并不需要知道什么被创建。
②当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品组中的对象。
③增加新的具体工厂和具体产品族很方便,符合“开闭原则”。
缺点:添加新的产品对象时,难以拓展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,所以修改起来会带来较大的不便。例:抽象操作系统中定义了生产按钮,窗口产品等级结构的的方法,但是此时想增加一个文本框产品品等级结构,那么就需要修改抽象操作系统,打破了开闭原则。即抽象工厂模式对于产品族的新增符合“开闭原则”,而对产品等级结构的新增不符合“开闭原则”,这种性质也称为“开闭原则”的倾斜性。
问题解决:在上一篇文章中,遇到了不能通过类名来生成对象的尴尬问题,本次也遇到了,通过上网搜索,最后在StackOverflow中找到了解决答案,原来是在使用Class.forName()时,其中的从参数应该写完全(包括所在包),在这里我的className节点中的内容是 LinuxFactory 导致了如下问题:
修改:改成className结点内容改为:com.abstractfactory.hing.LinuxFactory;问题得以解决