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

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

嘿,朋友!你是不是也遇到过代码里满是 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

总结

  • 没有银弹:别为了用模式而用。如果项目简单,一个简单工厂就足够了。过度设计反而会增加复杂性。
  • 关注扩展点:在设计之初,就想清楚哪些地方未来可能会变,把这些变化点封装起来,通常就是工厂模式的用武之地。
  • 别害怕多写代码 :工厂模式确实会增加一些类,但这些额外的代码换来的是更强的可维护性扩展性。这是非常值得的投入。
相关推荐
计算机毕业设计木哥4 小时前
计算机毕设选题:基于Python+Django的B站数据分析系统的设计与实现【源码+文档+调试】
java·开发语言·后端·python·spark·django·课程设计
失散134 小时前
分布式专题——1.2 Redis7核心数据结构
java·数据结构·redis·分布式·架构
用户3721574261354 小时前
Python 实现 HTML 转 Word 和 PDF
java
a587694 小时前
Java核心概念精讲:TCP与UDP的区别、Java NIO的几个核心组件与HTTP和HTTPS的区别等(46-50)
java·面试·nio
烛阴4 小时前
【TS 设计模式完全指南】用工厂方法模式打造你的“对象生产线”
javascript·设计模式·typescript
渣哥4 小时前
ConcurrentHashMap 的 get 要不要加锁?一次“多此一举”的心路历程
java
愿你天黑有灯下雨有伞4 小时前
一种基于注解与AOP的Spring Boot接口限流防刷方案
java·spring boot·后端
MuMuMu#5 小时前
JAVA NIO学习笔记基础强化学习总结
java·学习·nio
拾忆,想起5 小时前
Redis复制延迟全解析:从毫秒到秒级的优化实战指南
java·开发语言·数据库·redis·后端·缓存·性能优化