揭秘设计模式:工厂模式的五级进化之路

揭秘设计模式:工厂模式的五级进化之路

嘿,朋友!你是不是也遇到过代码里满是 new 关键字,每次加个新功能都得改一大堆老代码的窘境?别担心,这几乎是每个程序员的必经之路。今天,我就带你走一趟工厂模式的进化之旅,从最基础的"硬编码"到高手常用的"自动化"工厂,保证让你豁然开朗,写出更优雅、更易于扩展的代码。


第 0 级:原始时代------硬编码的痛

一开始,我们都喜欢简单粗暴。比如,给咖啡馆写个订单系统,顾客点什么,我们就直接在代码里 new 一个对应的对象。

代码长这样:

Java

csharp 复制代码
public class OrderService {
    public void takeOrder(String coffeeType) {
        Coffee coffee;
        if ("americano".equals(coffeeType)) {
            coffee = new Americano();
        } else if ("latte".equals(coffeeType)) {
            coffee = new Latte();
        } 
        coffee.prepare();
    }
}

问题显而易见:

  • 一改全动 :想加个新咖啡?对不起,你得去改 OrderService 的老代码。这就像往一栋建好的房子里塞新房间,风险太大。
  • 职责混乱OrderService 的本职是处理订单,而不是操心怎么制作咖啡。它现在既是服务员又是咖啡师,分工不明确。

第 1 级:启蒙时代------简单工厂

为了解决这个痛点,我们把创建咖啡的活儿交给一个专门的"咖啡机"------简单工厂

核心思想:用一个专门的工厂类来统一管理对象的创建。

UML 图

java 复制代码
// 简单工厂
public class SimpleCoffeeFactory {
    public static Coffee createCoffee(String type) {
        if ("americano".equals(type)) {
            return new Americano();
        } else if ("latte".equals(type)) {
            return new Latte();
        }
        return null;
    }
}

// 客户端代码
public class OrderService {
    public void takeOrder(String coffeeType) {
        Coffee coffee = SimpleCoffeeFactory.createCoffee(coffeeType);
        if (coffee != null) {
            coffee.prepare();
        }
    }
}

适用场景

  • 产品数量少且稳定:当你的应用只有少数几种产品,并且这些产品类型很少变动时,简单工厂是最高效的选择。

不足

  • 工厂臃肿 :如果咖啡种类越来越多,CoffeeFactory 里的 if-else 链会越来越长,变成一个难以维护的"巨无霸"。

第 2 级:工业时代------工厂方法

当咖啡馆的菜单每周都更新时,简单工厂的弊端就暴露了。我们得想个更灵活的办法。

核心思想:让每个产品都有自己的专属工厂。

UML 图

java 复制代码
// 抽象工厂接口
public interface CoffeeFactory {
    Coffee createCoffee();
}

// 具体工厂
public class AmericanoFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new Americano();
    }
}

public class LatteFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new Latte();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        CoffeeFactory factory = new AmericanoFactory();
        Coffee coffee = factory.createCoffee();
        coffee.prepare();
    }
}

适用场景

  • 需要频繁添加新产品:当你的应用需要经常扩展新的产品类型,并且希望在不修改旧代码的情况下实现。

不足

  • 类太多:每加一个产品就多两个类(产品和工厂),项目文件列表会越来越长。
  • 无法创建套餐:如果你想卖"咖啡+甜点"的套餐,工厂方法就无能为力了。

第 3 级:连锁经营------抽象工厂

我们的咖啡馆现在有了套餐服务:美式套餐配芝士蛋糕,拿铁套餐配曲奇。这些产品需要成套生产。

核心思想 :一个工厂负责创建一整套相关产品。

UML 图

java 复制代码
// 抽象产品
public interface Dessert { void serve(); }
public class Cheesecake implements Dessert { ... }
public class Cookie implements Dessert { ... }

// 抽象工厂
public interface ComboFactory {
    Coffee createCoffee();
    Dessert createDessert();
}

// 具体工厂
public class AmericanoComboFactory implements ComboFactory {
    @Override
    public Coffee createCoffee() { return new Americano(); }
    @Override
    public Dessert createDessert() { return new Cheesecake(); }
}

适用场景

  • 创建一系列相关或相互依赖的对象:当你的系统需要创建一整套"产品族",并且这些产品必须一起使用时。

不足

  • 扩展性受限 :如果需要新增一个产品种类(如"小食"),你必须修改所有抽象工厂和具体工厂,工作量不小。
  • 客户端依赖具体工厂:客户端代码仍然需要知道具体工厂的类名。

第 4 级:自动化时代------注册式工厂

现在,我们希望我们的系统足够智能,能根据配置文件动态选择工厂,让代码彻底告别 new 的烦恼。

核心思想:用一个中央注册中心来管理所有工厂。

UML 图

java 复制代码
// 工厂注册中心(单例模式实现)
public class FactoryRegistry {
    private static final Map<String, ComboFactory> factories = new HashMap<>();

    public static void register(String name, ComboFactory factory) {
        factories.put(name, factory);
    }

    public static ComboFactory getFactory(String name) {
        return factories.get(name);
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 程序启动时注册所有工厂
        FactoryRegistry.register("americano_combo", new AmericanoComboFactory());
        FactoryRegistry.register("latte_combo", new LatteComboFactory());

        // 客户端通过名称获取工厂,完全不依赖具体类名
        ComboFactory factory = FactoryRegistry.getFactory("americano_combo");
        Coffee coffee = factory.createCoffee();
        Dessert dessert = factory.createDessert();
    }
}

适用场景

  • 运行时动态加载:需要在运行时动态地选择和创建工厂,例如根据配置文件或用户输入来决定使用哪种工厂。
  • 彻底解耦:当需要将客户端与具体工厂类彻底解耦时,以实现更强的可扩展性和可维护性。

开发框架与业务中的应用

你可能觉得这些理论听起来很酷,但离实际工作很远。其实不然,工厂模式无处不在。

  • Spring Framework :Spring 的核心就是工厂模式的集大成者。它的 IoC 容器 (比如 ApplicationContext)就是一个超级工厂,负责管理、创建和装配所有对象(bean)。你只需要通过注解或配置告诉它怎么做,剩下的事情它都帮你搞定。
  • 支付系统:一个好的支付系统会为每个支付渠道(支付宝、微信、银联)设计一个抽象工厂,用来创建各自的支付网关、签名工具等。想接入一个新的支付渠道?轻松!加一套新的工厂实现就行。
  • 跨平台 GUI 开发 :在跨平台图形用户界面(GUI)框架中,抽象工厂模式 用于创建不同操作系统的组件族。例如,一个 GUIFactory 接口可能包含 createButton()createCheckbox() 方法。在 Windows 上,使用 WindowsGUIFactory 来创建 WindowsButtonWindowsCheckbox;在 macOS 上,则使用 MacOSGUIFactory 来创建 MacOSButtonMacOSCheckbox

总结

  • 没有银弹:别为了用模式而用。如果项目简单,一个简单工厂就足够了。过度设计反而会增加复杂性。
  • 关注扩展点:在设计之初,就想清楚哪些地方未来可能会变,把这些变化点封装起来,通常就是工厂模式的用武之地。
  • 别害怕多写代码 :工厂模式确实会增加一些类,但这些额外的代码换来的是更强的可维护性扩展性。这是非常值得的投入。
相关推荐
我不是混子11 小时前
什么是Java 的 Lambda 表达式?
java·后端
小蝙蝠侠11 小时前
JMeter 执行流程
java·jmeter
某不知名網友12 小时前
Reactor 模式:高并发网络编程的事件驱动利器
网络·设计模式·php
Asort12 小时前
JavaScript设计模式(六)——适配器模式 (Adapter)
前端·javascript·设计模式
程序员小假12 小时前
我们来说一说 ThreadLocal 内存泄漏
java·后端
xq952712 小时前
获取Facebook 散列利器 来了 十六进制到 Base64 转换器
java
我不是混子12 小时前
聊聊Spring事件机制
java·后端
DKPT12 小时前
JVM栈溢出时如何dump栈信息?
java·jvm·笔记·学习·spring
DKPT12 小时前
JVM堆大小如何设置?
java·开发语言·jvm·笔记·学习
铅笔侠_小龙虾12 小时前
JVM 目录
java·jvm