基于本文回答
0
评论

设计模式--工厂模式 (Factory)

知识点图片

本文系统讲解了工厂设计模式,其核心是将对象创建与使用分离。文章详细介绍了简单工厂、工厂方法和抽象工厂三种类型的思想、结构与优缺点,并对比了各自的应用场景。

我们来详细、系统地讲解设计模式中的工厂模式(Factory Pattern)

工厂模式是一种创建型设计模式,其核心思想是将对象的创建过程与使用过程分离。客户端不需要知道具体要创建哪个类的实例,也无需关心创建的复杂细节,只需要向一个“工厂”索要一个“产品”,工厂会根据你的需求返回相应的产品实例。

这就像去快餐店点餐:你只需要告诉收银员你要“汉堡”还是“鸡翅”(这是你的需求),而不需要关心厨房里是如何用面粉、肉、蔬菜等原材料制作出这些食物的(这是创建细节)。收银员和厨房就扮演了“工厂”的角色。

工厂模式主要分为三种类型,由简单到复杂,解决的问题也越来越抽象:

  1. 简单工厂模式(Simple Factory Pattern) - 又称静态工厂方法模式。
  2. 工厂方法模式(Factory Method Pattern) - 符合设计原则的经典工厂模式。
  3. 抽象工厂模式(Abstract Factory Pattern) - 工厂的工厂,用于创建产品族。

1. 简单工厂模式 (Simple Factory Pattern)

简单工厂模式虽然不属于 GoF(《设计模式》四人组)定义的 23 种经典设计模式之一,但它是理解工厂模式的绝佳入门。

核心思想

创建一个专门的工厂类,该类有一个静态方法,根据传入的参数来决定创建并返回哪种产品类的实例。

结构角色

  • Factory (工厂类):核心角色,负责实现创建所有产品实例的内部逻辑。它提供一个静态方法,根据客户端请求(参数)返回不同的产品对象。
  • Product (抽象产品接口):定义了所有产品需要实现的公共接口。
  • ConcreteProduct (具体产品类):实现了产品接口,是工厂类创建的目标。

示例:支付方式选择

假设一个电商系统需要支持多种支付方式,如支付宝、微信支付。

代码实现 (Java):

java
// 1. 抽象产品接口 (Payment)
public interface Payment {
    void pay();
}

// 2. 具体产品类 (Alipay, WeChatPay)
public class Alipay implements Payment {
    @Override
    public void pay() {
        System.out.println("使用支付宝支付...");
    }
}

public class WeChatPay implements Payment {
    @Override
    public void pay() {
        System.out.println("使用微信支付...");
    }
}

// 3. 工厂类 (PaymentFactory)
public class PaymentFactory {
    // 静态方法,根据类型创建产品
    public static Payment createPayment(String type) {
        if ("alipay".equalsIgnoreCase(type)) {
            return new Alipay();
        } else if ("wechat".equalsIgnoreCase(type)) {
            return new WeChatPay();
        } else {
            return null; // 或者抛出异常
        }
    }
}

// 4. 客户端调用
public class Client {
    public static void main(String[] args) {
        Payment payment = PaymentFactory.createPayment("wechat");
        if (payment != null) {
            payment.pay(); // 输出: 使用微信支付...
        }
    }
}

优缺点

  • 优点
    • 结构简单,易于理解和实现。
    • 将对象的创建和使用分离,客户端只依赖工厂和产品接口,不依赖具体产品类。
  • 缺点
    • 违反了开闭原则(Open/Closed Principle)。每当需要增加一种新的支付方式(如银联支付),就必须修改 PaymentFactory 类的 createPayment 方法,增加 else if 分支。
    • 工厂类职责过重,包含了所有产品的创建逻辑,一旦工厂类出问题,整个系统都会受影响。

2. 工厂方法模式 (Factory Method Pattern)

工厂方法模式是 GoF 的经典模式之一,它解决了简单工厂模式违反开闭原则的问题。

核心思想

定义一个用于创建对象的接口(或抽象类),但让子类来决定到底要实例化哪一个类。工厂方法将一个类的实例化延迟到其子类中进行。

结构角色

  • Product (抽象产品接口):与简单工厂模式相同。
  • ConcreteProduct (具体产品类):与简单工厂模式相同。
  • Factory (抽象工厂):定义一个创建产品的抽象方法(工厂方法),返回一个产品对象。
  • ConcreteFactory (具体工厂):实现抽象工厂接口,重写工厂方法,返回一个具体的的产品实例。

示例:支付方式选择(重构版)

代码实现 (Java):

java
// 产品接口和具体产品类 (Payment, Alipay, WeChatPay) 保持不变

// 3. 抽象工厂 (PaymentFactory)
public interface PaymentFactory {
    Payment createPayment();
}

// 4. 具体工厂 (AlipayFactory, WeChatPayFactory)
public class AlipayFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new Alipay();
    }
}

public class WeChatPayFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new WeChatPay();
    }
}

// 5. 客户端调用
public class Client {
    public static void main(String[] args) {
        // 需要支付宝支付,就创建支付宝工厂
        PaymentFactory alipayFactory = new AlipayFactory();
        Payment alipay = alipayFactory.createPayment();
        alipay.pay(); // 输出: 使用支付宝支付...

        // 需要微信支付,就创建微信工厂
        PaymentFactory wechatFactory = new WeChatPayFactory();
        Payment wechatPay = wechatFactory.createPayment();
        wechatPay.pay(); // 输出: 使用微信支付...
    }
}

优缺点

  • 优点
    • 完美遵循开闭原则。如果需要增加一种新的支付方式(如银联支付),只需增加一个 UnionPay 类和一个 UnionPayFactory 类即可,无需修改任何现有代码。
    • 将产品创建的责任下放到具体的工厂子类,实现了更好的解耦。
    • 每个具体工厂只负责创建一个具体产品,职责单一。
  • 缺点
    • 每增加一个产品,就需要增加一个对应的工厂类,导致系统中的类数量成倍增加,增加了系统的复杂度和代码量。

3. 抽象工厂模式 (Abstract Factory Pattern)

抽象工厂模式是工厂模式中最复杂的一种,它处理的是“产品族(Product Family)”的创建问题。

核心思想

提供一个接口,用于创建一系列相关或相互依赖的对象(即一个产品族),而无需指定它们具体的类。

场景解释

想象一下为不同操作系统(Windows, macOS)开发一套 UI 组件(按钮、文本框)。

  • 产品族1(Windows 风格): WindowsButton, WindowsTextField
  • 产品族2(macOS 风格): MacButton, MacTextField

一个工厂需要能同时生产属于同一个风格(产品族)的所有组件。比如,Windows 工厂就应该生产 Windows 风格的按钮和文本框,而不能混搭。

结构角色

  • AbstractFactory (抽象工厂):声明了一组用于创建不同类型抽象产品的方法。
  • ConcreteFactory (具体工厂):实现了抽象工厂的接口,负责创建某个具体产品族的全部产品。
  • AbstractProduct (抽象产品):为一类产品对象声明一个接口。
  • ConcreteProduct (具体产品):定义了具体工厂要创建的产品对象。

示例:跨平台 UI 组件库

代码实现 (Java):

java
// 1. 抽象产品接口 (Button, TextField)
public interface Button { void display(); }
public interface TextField { void display(); }

// 2. 具体产品类 (WindowsButton, MacButton, etc.)
public class WindowsButton implements Button {
    public void display() { System.out.println("显示 Windows 风格按钮"); }
}
public class WindowsTextField implements TextField {
    public void display() { System.out.println("显示 Windows 风格文本框"); }
}
public class MacButton implements Button {
    public void display() { System.out.println("显示 macOS 风格按钮"); }
}
public class MacTextField implements TextField {
    public void display() { System.out.println("显示 macOS 风格文本框"); }
}

// 3. 抽象工厂 (GUIFactory)
public interface GUIFactory {
    Button createButton();
    TextField createTextField();
}

// 4. 具体工厂 (WindowsFactory, MacFactory)
public class WindowsFactory implements GUIFactory {
    public Button createButton() { return new WindowsButton(); }
    public TextField createTextField() { return new WindowsTextField(); }
}
public class MacFactory implements GUIFactory {
    public Button createButton() { return new MacButton(); }
    public TextField createTextField() { return new MacTextField(); }
}

// 5. 客户端调用
public class Application {
    private Button button;
    private TextField textField;

    public Application(GUIFactory factory) {
        button = factory.createButton();
        textField = factory.createTextField();
    }

    public void renderUI() {
        button.display();
        textField.display();
    }

    public static void main(String[] args) {
        // 根据当前操作系统环境选择合适的工厂
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory factory;
        if (osName.contains("win")) {
            factory = new WindowsFactory();
        } else {
            factory = new MacFactory();
        }
        
        Application app = new Application(factory);
        app.renderUI();
    }
}

优缺点

  • 优点
    • 隔离了具体类的实现。客户端只与抽象工厂和抽象产品接口打交道。
    • 易于切换产品族。只需要改变具体工厂的实例,就可以改变整个应用的行为。
    • 保证了产品之间的一致性。当使用一个具体工厂时,它创建的产品一定是属于同一个产品族的,不会出现混用(如 Windows 按钮配 macOS 文本框)的情况。
  • 缺点
    • 难以扩展新的产品类型。如果想给产品族增加一个新的产品(如 Checkbox),就需要修改 GUIFactory 抽象工厂接口,这会导致所有已有的具体工厂子类都需要进行修改,违反了开闭原则。

三种工厂模式的总结与对比

特性 简单工厂模式 工厂方法模式 抽象工厂模式
目的 创建一种产品 创建一种产品 创建一族(多个相关)产品
复杂性
关注点 如何创建对象 将创建过程延迟到子类 创建一个产品家族,确保产品间的兼容性
开闭原则 违反(增加新产品需修改工厂) 遵循(增加新产品只需增加新工厂) 对于新产品族遵循,对于新产品类型违反
抽象层次 只有一个工厂类,没有抽象层 有抽象工厂和具体工厂 有抽象工厂、具体工厂、抽象产品、具体产品
适用场景 创建的对象较少,且不经常变化 一个类不知道它所需要的对象的类 系统需要与多个产品系列中的一个系列进行交互

总而言之,工厂模式是解耦对象创建与使用的利器。从简单工厂到工厂方法,再到抽象工厂,模式的复杂度和抽象度不断提升,以应对更加复杂的对象创建场景。在实际开发中,应根据具体需求选择最合适的工厂模式。

右滑查看面试常问