跳转到内容

抽象工厂 (Abstract Factory)

抽象工厂 (Abstract Factory) 是一种创建型设计模式:它提供的是“一整族产品”的创建接口,而不是单个产品。一族里的多个对象(例如按钮、输入框、弹窗)在风格或语义上是一套的,必须一起用、不能混搭。抽象工厂让调用方只依赖“某族工厂”的抽象,由具体工厂负责造出整族的具体产品,这样既能保证族内一致,又便于切换整族或扩展新族。

系统里经常有多类相关对象要配套使用。例如校内系统里,学生端和管理端用两套界面风格:学生端用圆角、浅色、大按钮,管理端用直角、深色、紧凑控件。如果业务代码里到处自己 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 内部逻辑。

  • 工厂方法:一个工厂方法只负责一种产品,子类决定这种产品的具体类型(例如导出器子类决定导出成绩单还是缴费凭证)。
  • 抽象工厂:一个工厂负责一族多种产品,提供多个创建方法(如 createButton()createTextBox()),一次把整族对象配齐,保证族内风格或语义一致。

可以理解为:工厂方法是“一种产品、多态创建”;抽象工厂是“一族产品、一起创建”。

抽象工厂通过“工厂的抽象”和“产品族的抽象”,让客户端与具体产品族解耦。要换整组相关对象(例如换学生端 / 管理端界面、换一套数据访问组合)时,只需换一个工厂实例;要扩展新的产品族,就新增具体工厂和对应产品类,无需改已有调用代码,符合开闭原则。