大话设计模式十五:抽象工厂模式(abstract factory)

一. 定义:

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

其实它和工厂方法很像,工厂方法是一个,而抽象工厂是一系列。


二. 结构和说明:


AbstractFactory:抽象工厂,定义创建一系列产品对象的操作接口。

ConcreteFactory:具体的工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建。

AbstractProduct:定义一类产品对象的接口。

ConcreteProduct:具体的产品实现对象,通常在具体工厂里面,或选择具体的产品实现对象,来创建负荷抽象工厂定义的方法返回的产品类型的对象。

Client:客户端,主要使用抽象工厂来获取一系列所需要的产品对象,然后面向这些产品对象的接口编程,以实现需要的功能。

大话设计模式十五:抽象工厂模式(abstract factory)_第1张图片

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. 不太容易扩展新的产品

如果一个系统要需要动态的切换产品族的时候需要用抽象工厂模式

如果要强调一系列相关产品的接口,以便联合使用它们的时候用抽象工厂模式。


你可能感兴趣的:(设计模式,抽象工厂,开放封闭)