抽象工厂 (Abstract Factory)
抽象工厂 (Abstract Factory) 是一种创建型设计模式:它提供的是“一整族产品”的创建接口,而不是单个产品。一族里的多个对象(例如按钮、输入框、弹窗)在风格或语义上是一套的,必须一起用、不能混搭。抽象工厂让调用方只依赖“某族工厂”的抽象,由具体工厂负责造出整族的具体产品,这样既能保证族内一致,又便于切换整族或扩展新族。
问题从哪来?
Section titled “问题从哪来?”系统里经常有多类相关对象要配套使用。例如校内系统里,学生端和管理端用两套界面风格:学生端用圆角、浅色、大按钮,管理端用直角、深色、紧凑控件。如果业务代码里到处自己 new 按钮、输入框、弹窗,很容易出现“学生端页面里混进一个管理端风格的按钮”,或者“换一套风格时要在几十个调用点改代码”的情况。
也就是说:一族产品要一起造、一起换,但若分散在各地各自 new 具体类,就既难保证一致,又难扩展新族。需要有一层“族工厂”,由它统一负责造出这一族的全部组件。
- 抽象工厂接口:定义一族里每种产品的创建方法(如
createButton()、createTextBox()),返回的是抽象产品类型,不暴露具体类。 - 具体工厂:每个具体工厂实现该接口,负责造“某一族”的全部具体产品(例如学生端工厂造学生端按钮 + 学生端文本框,管理端工厂造管理端按钮 + 管理端文本框)。
- 客户端只拿工厂:调用方只依赖抽象工厂和抽象产品,通过注入的一个工厂实例拿到整族对象;要换风格就换一个工厂实例,不必改业务逻辑。
- 扩展新族:新增一套风格或一套业务族时,增加新的具体工厂和对应的具体产品类即可,原有客户端代码不用动。
classDiagram
class AbstractFactory {
<<interface>>
+createProductA()* AbstractProductA
+createProductB()* AbstractProductB
}
class ConcreteFactory1 {
+createProductA() AbstractProductA
+createProductB() AbstractProductB
}
class ConcreteFactory2 {
+createProductA() AbstractProductA
+createProductB() AbstractProductB
}
class AbstractProductA {
<<interface>>
}
class AbstractProductB {
<<interface>>
}
class ProductA1 {
}
class ProductA2 {
}
class ProductB1 {
}
class ProductB2 {
}
AbstractFactory <|.. ConcreteFactory1
AbstractFactory <|.. ConcreteFactory2
AbstractFactory ..> AbstractProductA : creates
AbstractFactory ..> AbstractProductB : creates
AbstractProductA <|.. ProductA1
AbstractProductA <|.. ProductA2
AbstractProductB <|.. ProductB1
AbstractProductB <|.. ProductB2
ConcreteFactory1 ..> ProductA1 : creates
ConcreteFactory1 ..> ProductB1 : creates
ConcreteFactory2 ..> ProductA2 : creates
ConcreteFactory2 ..> ProductB2 : creates
Java 示例:学生端 / 管理端两套界面组件
Section titled “Java 示例:学生端 / 管理端两套界面组件”用校内系统的两套界面风格举例:一族是学生端(圆角、浅色),一族是管理端(直角、深色)。每族都有按钮和文本框,由同一个工厂造出来,保证风格一致。
抽象产品与具体产品:
// 抽象产品:按钮public interface Button { void render();}
public class StudentPortalButton implements Button { @Override public void render() { System.out.println("学生端风格按钮:圆角、浅色"); }}
public class AdminPortalButton implements Button { @Override public void render() { System.out.println("管理端风格按钮:直角、深色"); }}
// 抽象产品:文本框public interface TextBox { void render();}
public class StudentPortalTextBox implements TextBox { @Override public void render() { System.out.println("学生端风格文本框:圆角、浅色"); }}
public class AdminPortalTextBox implements TextBox { @Override public void render() { System.out.println("管理端风格文本框:直角、深色"); }}抽象工厂与具体工厂:
public interface PortalUIFactory { Button createButton(); TextBox createTextBox();}
public class StudentPortalUIFactory implements PortalUIFactory { @Override public Button createButton() { return new StudentPortalButton(); }
@Override public TextBox createTextBox() { return new StudentPortalTextBox(); }}
public class AdminPortalUIFactory implements PortalUIFactory { @Override public Button createButton() { return new AdminPortalButton(); }
@Override public TextBox createTextBox() { return new AdminPortalTextBox(); }}客户端只依赖抽象工厂:
public class PortalApplication { private final Button button; private final TextBox textBox;
public PortalApplication(PortalUIFactory factory) { this.button = factory.createButton(); this.textBox = factory.createTextBox(); }
public void paint() { button.render(); textBox.render(); }
public static void main(String[] args) { // 学生端:整族都是学生端风格 PortalUIFactory studentFactory = new StudentPortalUIFactory(); PortalApplication studentApp = new PortalApplication(studentFactory); studentApp.paint();
// 管理端:整族都是管理端风格,只需换工厂 PortalUIFactory adminFactory = new AdminPortalUIFactory(); PortalApplication adminApp = new PortalApplication(adminFactory); adminApp.paint(); }}客户端只持有一个 PortalUIFactory,通过它拿按钮和文本框,保证这一屏上的控件来自同一族。要换学生端 / 管理端,只需换传入的工厂实现,不必改 PortalApplication 内部逻辑。
与工厂方法的区别
Section titled “与工厂方法的区别”- 工厂方法:一个工厂方法只负责一种产品,子类决定这种产品的具体类型(例如导出器子类决定导出成绩单还是缴费凭证)。
- 抽象工厂:一个工厂负责一族多种产品,提供多个创建方法(如
createButton()、createTextBox()),一次把整族对象配齐,保证族内风格或语义一致。
可以理解为:工厂方法是“一种产品、多态创建”;抽象工厂是“一族产品、一起创建”。
抽象工厂通过“工厂的抽象”和“产品族的抽象”,让客户端与具体产品族解耦。要换整组相关对象(例如换学生端 / 管理端界面、换一套数据访问组合)时,只需换一个工厂实例;要扩展新的产品族,就新增具体工厂和对应产品类,无需改已有调用代码,符合开闭原则。