一. 定义:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
其实它和工厂方法很像,工厂方法是一个,而抽象工厂是一系列。
二. 结构和说明:
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. 不太容易扩展新的产品
如果一个系统要需要动态的切换产品族的时候需要用抽象工厂模式
如果要强调一系列相关产品的接口,以便联合使用它们的时候用抽象工厂模式。