一. 定义:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
其实它和工厂方法很像,工厂方法是一个,而抽象工厂是一系列。
二. 结构和说明:
AbstractFactory:抽象工厂,定义创建一系列产品对象的操作接口。
ConcreteFactory:具体的工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建。
AbstractProduct:定义一类产品对象的接口。
ConcreteProduct:具体的产品实现对象,通常在具体工厂里面,或选择具体的产品实现对象,来创建负荷抽象工厂定义的方法返回的产品类型的对象。
Client:客户端,主要使用抽象工厂来获取一系列所需要的产品对象,然后面向这些产品对象的接口编程,以实现需要的功能。
1. AbstractFactory:
/** * 抽象工厂的接口,声明创建抽象产品对象的操作 */ public interface AbstractFactory { /** * 示例方法,创建抽象产品A的对象 * @return 抽象产品A的对象 */ public AbstractProductA createProductA(); /** * 示例方法,创建抽象产品B的对象 * @return 抽象产品B的对象 */ public AbstractProductB createProductB(); }2. ConcreteFactory1:
/** * 具体的工厂实现对象,实现创建具体的产品对象的操作 */ public class ConcreteFactory1 implements AbstractFactory { public AbstractProductA createProductA() { return new ProductA1(); } public AbstractProductB createProductB() { return new ProductB1(); } }3. ConcreteFactory2:
/** * 具体的工厂实现对象,实现创建具体的产品对象的操作 */ public class ConcreteFactory2 implements AbstractFactory { public AbstractProductA createProductA() { return new ProductA2(); } public AbstractProductB createProductB() { return new ProductB2(); } }4. AbstractProductA:
/** * 抽象产品A的接口 */ public interface AbstractProductA { //定义抽象产品A相关的操作 }5. AbstractProductB:
/** * 抽象产品B的接口 */ public interface AbstractProductB { //定义抽象产品B相关的操作 }6. ProductA1:
/** * 产品A的具体实现 */ public class ProductA1 implements AbstractProductA { //实现产品A的接口中定义的操作 }7. ProductA2:
/** * 产品A的具体实现 */ public class ProductA2 implements AbstractProductA { //实现产品A的接口中定义的操作 }8. ProductB1:
/** * 产品B的具体实现 */ public class ProductB1 implements AbstractProductB { //实现产品B的接口中定义的操作 }9. ProductB2:
/** * 产品B的具体实现 */ public class ProductB2 implements AbstractProductB { //实现产品B的接口中定义的操作 }
三. 实例:
假设现在我们现在需要用java实现一个给页面换皮肤的功能,即红色的按钮需要搭配红色的输入框;绿色的按钮需要搭配绿色的输入框。
我们用抽象工厂模式来设计:那么红色按钮和红色输入框就是一个产品族了。
不使用模式的解决方案:
1. 定义按钮工厂
public class ButtonFactory { public static Button createButton(int type) { if(type == 1){ return new GreenButton(); }else if(type == 2){ return new RedButton(); } return null; } }2. 定义输入框工厂:
public class TextFieldFactory { public static TextField createTextField(int type) { if(type == 1){ return new GreenTextField(); }else if(type == 2){ return new RedTextField(); } return null; } }
3. 按钮接口:
//按钮接口 public interface Button { public void display(); }4. 红色按钮实现:
//红色按钮类:具体产品 public class RedButton implements Button { public void display() { System.out.println("显示红色按钮"); } }5. 绿色按钮实现:
//绿色按钮类:具体产品 public class GreenButton implements Button { public void display() { System.out.println("显示绿色按钮。"); } }6. 输入框接口:
//文本框接口:抽象产品 public interface TextField { public void display(); }7. 红色输入框实现:
//红色文本框类:具体产品 public class RedTextField implements TextField { public void display() { System.out.println("显示红色文本框"); } }8. 绿色输入框实现:
//绿色文本框类:具体产品 public class GreenTextField implements TextField { public void display() { System.out.println("显示绿色文本框。"); } }
9. 测试类:
public class Client { public static void main(String args[]) { Button button = ButtonFactory.createButton(1); TextField textField = TextFieldFactory.createTextField(1); button.display(); textField.display(); } }
虽然也能实现业务,但是这个代码有个问题,它需要调用者在传参的时候要小心,如果第一个参数是1,第二个参数是2,那就错了。
而且还有个问题就是不好拓展,如果我再加一种蓝色皮肤,那可就要改ButtonFactory和TextFieldFactory了,很不方便
使用模式的解决方案1:
1. 定义皮肤抽象工厂:
//皮肤工厂接口:抽象工厂 public interface SkinFactory { public Button createButton(); public TextField createTextField(); }2. 红色皮肤工厂:
//红色皮肤工厂:具体工厂 public class RedSkinFactory implements SkinFactory { public Button createButton() { return new RedButton(); } public TextField createTextField() { return new RedTextField(); } }3. 绿色皮肤工厂:
//绿色皮肤工厂:具体工厂 public class GreenSkinFactory implements SkinFactory { public Button createButton() { return new GreenButton(); } public TextField createTextField() { return new GreenTextField(); } }
4. 读取并解析配置文件类:
public class XMLUtil { // 该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象 public static Object getBean() { try { // 创建文档对象 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new File("config.xml")); // 获取包含类名的文本节点 NodeList list = doc.getElementsByTagName("className"); Node classNode = list.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; } }5. config.xml
<?xml version="1.0"?> <config> <className>com.zdp.abstractfactory.RedSkinFactory</className> </config>6. 客户端:
public class Client { public static void main(String args[]) { // 使用抽象层定义 SkinFactory factory = (SkinFactory) XMLUtil.getBean(); Button button = factory.createButton(); TextField textField = factory.createTextField(); button.display(); textField.display(); } }
抽象工厂定义了一个产品族,如果想切换产品族(比如换成“绿色风格”,只需要改下配置文件就好了)。
而且也很好拓展,例如加一种“蓝色”风格的皮肤,只需要新增BlueSkinFactory实现SkinFactory,BlueButton实现Button,BlueTextField实现TextField。
使用模式的解决方案2:
方案1其实有个不太好的地方,如果在产品族中要添加一种新产品,比如下拉框,那么我就要修改SkinFactory这个接口了,一般情况下,接口是不能轻易修改的,因为它是一种规范,一个契约。因为接口一改,所有的实现都要改。
下面我们使用一种相对灵活,但是不太安全(需要强转)的方法来解决:在抽象工厂中只定义一个方法,给这个方法设置一个参数,通过这个参数来判断具体创建什么产品对象。
1. SkinFactory:
//皮肤工厂接口:抽象工厂 public interface SkinFactory { public Object createComponent(Component component); }2. RedSkinFactory:
public class RedSkinFactory implements SkinFactory { @Override public Object createComponent(Component component) { switch (component) { case BUTTON: return new RedButton(); case TEXT_FIELD: return new RedTextField(); } return null; } }3. GreenSkinFactory:
public class GreenSkinFactory implements SkinFactory { @Override public Object createComponent(Component component) { switch(component){ case BUTTON: return new GreenButton(); case TEXT_FIELD: return new GreenTextField(); } return null; } }4. 枚举类:
public enum Component { BUTTON, TEXT_FIELD; }5. 测试类:
public class Client { public static void main(String args[]) { SkinFactory factory = new RedSkinFactory(); Button button = (Button)factory.createComponent(Component.BUTTON); TextField textField = (TextField)factory.createComponent(Component.TEXT_FIELD); button.display(); textField.display(); } }
现在如果要加入一个下拉框的话,我们只需要修改SkinFactory的实现:RedSkinFactory和GreenSkinFactory就可以了,而无需动接口。
四. 优缺点:
1. 分离接口和实现
2. 切换产品族变得容易
3. 不太容易扩展新的产品
如果一个系统要需要动态的切换产品族的时候需要用抽象工厂模式
如果要强调一系列相关产品的接口,以便联合使用它们的时候用抽象工厂模式。