抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类,它是一种对象创建型模式
类图
AbstractProductA和AbstractProductB是两个抽象产品
ConcreteProduct就是对两个抽象产品的具体分类的实现
AbstractFacroty是抽象工厂接口,里面包含所有产品创建的抽象方法,而ConcreteFactory就是具体工厂
应用实例
某软件公司要开发一套界面皮肤库,可以对基于Java的桌面软件进行界面美化。用户在使用时可以通过菜单来选择皮肤,不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等界面元素,例如春天(Spring)风格的皮肤将提供浅绿色的按钮、绿色边框的文本框和绿色边框的组合框,而夏天(Summer)风格的皮肤则提供浅蓝色的按钮、蓝色边框的文本框和蓝色边框的组合框,其结构示意图如下图所示:
该皮肤库需要具备良好的灵活性和可扩展性,用户可以自由选择不同的皮肤,开发人员可以在不修改既有代码的基础上增加新的皮肤。
现使用抽象工厂模式来设计该界面皮肤库。
代码实现
按钮接口,抽象产品
package design.abstractfactory;
public interface Button {
public void display();
}
按钮接口下的具体产品
package design.abstractfactory;
public class SpringButton implements Button {
@Override
public void display() {
System.out.println("显示浅绿色按钮.");
}
}
package design.abstractfactory;
public class SummerButton implements Button {
@Override
public void display() {
System.out.println("显示浅蓝色按钮.");
}
}
文本框接口,抽象产品
package design.abstractfactory;
public interface TextField {
public void display();
}
文本框接口下的具体产品
package design.abstractfactory;
public class SpringTextField implements TextField {
@Override
public void display() {
System.out.println("显示绿色边框文本框.");
}
}
package design.abstractfactory;
public class SummerTextField implements TextField {
@Override
public void display() {
System.out.println("显示蓝色边框文本框.");
}
}
组合框接口,抽象产品
package design.abstractfactory;
public interface ComboBox {
public void display();
}
组合框接口下的具体产品
package design.abstractfactory;
public class SpringComboBox implements ComboBox {
@Override
public void display() {
System.out.println("显示绿色边框组合框.");
}
}
package design.abstractfactory;
public class SummerComboBox implements ComboBox {
@Override
public void display() {
System.out.println("显示蓝色边框组合框.");
}
}
界面皮肤接口,充当抽象工厂
package design.abstractfactory;
public interface SkinFactory {
public Button createButton();
public TextField createTextField();
public ComboBox createComboBox();
}
具体工厂
package design.abstractfactory;
public class SpringSkinFactory implements SkinFactory {
@Override
public Button createButton() {
return new SpringButton();
}
@Override
public TextField createTextField() {
return new SpringTextField();
}
@Override
public ComboBox createComboBox() {
return new SpringComboBox();
}
}
package design.abstractfactory;
public class SummerSkinFactory implements SkinFactory {
@Override
public Button createButton() {
return new SummerButton();
}
@Override
public TextField createTextField() {
return new SummerTextField();
}
@Override
public ComboBox createComboBox() {
return new SummerComboBox();
}
}
配置文件config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<className>design.abstractfactory.SpringSkinFactory</className>
</config>
工具类
package design.abstractfactory;
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XMLUtil {
public static Object getBean() {
try {
// 创建DOM文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src//design//abstractfactory//config.xml"));
// 获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.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;
}
}
}
客户端
package design.abstractfactory;
public class Client {
public static void main(String[] args) {
// 使用抽象层定义
SkinFactory factory;
//SkinFactory factory1;
Button bt;
TextField tf;
ComboBox cb;
factory=(SkinFactory) XMLUtil.getBean();
bt=factory.createButton();
tf=factory.createTextField();
cb = factory.createComboBox();
bt.display();
tf.display();
cb.display();
}
}
如果需要更换皮肤,只需修改配置文件
若有项目需要将春天风格的下拉列表框与夏天风格的文本框和按钮搭配在一起使用,应当如何完成,试在上面的项目中实现
增加新的具体工厂类
package design.abstractfactory;
public class SpringSummerSkinFactory implements SkinFactory {
@Override
public Button createButton() {
return new SummerButton();
}
@Override
public TextField createTextField() {
return new SummerTextField();
}
@Override
public ComboBox createComboBox() {
return new SpringComboBox();
}
}
将配置文件的引用换成该工厂类
<?xml version="1.0" encoding="UTF-8"?>
<config>
<className>design.abstractfactory.SpringSummerSkinFactory</className>
</config>
运行客户端结果为
抽象工厂模式优缺点
优点:
(1)隔离了具体类的生成,使得客户端并不需要知道什么被创建
(2)当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
(3)增加新的产品族很方便,无须修改已有系统,符合开闭原则
缺点:
增加新的等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,违背开闭原则
适用环境
(1)一个系统不应当依赖于产品类实例如何创建、组合和表达的细节
(2)系统中有多于一个的产品族,而每次只使用其中某一产品族
(3)属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
(4)产品等级结构稳定,在设计完成之后不会向系统中增加新的产品等级结构或者删除已有的产品等级结构
工厂方法与抽象工厂对比
工厂方法一般只生产一种产品,核心是定义抽象方法,通过子类创建具体产品,不同的子类生产不同的产品;抽象工厂是生产一组产品,每个工厂子类都需要生产一组产品,并且每一种产品在每个子类都有不同的具体类型;
工厂方法潜伏在抽象工厂中。这很有道理,因为抽象工厂的任务是定义负责创建一组产品的接口,而这个接口的每个方法都负责创建一个具体产品,所以让工厂的子类实现这些方法。比如,对于SkinFactory,如果我们只看一种组件:按钮,在工厂接口中定义了创建按钮的方法 createButton(),子类实现该方法创建不同风格的按钮,即“由子类决定要实例化的类是哪一个”,完全符合工厂方法的定义。
具体表现:工厂方法只有一个抽象方法,抽象工厂拥有一个大的接口(或多个抽象方法);
共同点:
依赖倒置原则:都依赖抽象,而不依赖具体类;
类爆炸:尤其是抽象工厂,会使类显著增多;
适用场景:
抽象工厂:当需要创建产品家族和想让制造的相关产品集合起来时适用。
工厂方法:创建一种产品时,或者,可以把你的客户代码从需要实例化的具体类中解耦,再或者,你目前还不知道将来需要实例化哪些类,都可以使用。
小总结
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。
参考
java设计模式
掘金:https://juejin.cn/post/6844903783680442381
掘金:https://juejin.cn/post/6844903895760650254