【Java EE】工厂模式

工厂模式

  • [🍔 为什么需要工厂模式?](#🍔 为什么需要工厂模式?)
  • 简单工厂模式
    • [💡 核心思想](#💡 核心思想)
    • [🛠️ 代码实现](#🛠️ 代码实现)
    • [📊 优缺点分析](#📊 优缺点分析)
  • 工厂方法模式
    • [💡 核心思想](#💡 核心思想)
    • [🛠️ 代码实现](#🛠️ 代码实现)
    • [📊 优缺点分析](#📊 优缺点分析)
  • 抽象工厂模式
    • [💡 核心思想](#💡 核心思想)
    • [🛠️ 代码实现](#🛠️ 代码实现)
    • [📊 优缺点分析](#📊 优缺点分析)
  • [🧠 总结与对比](#🧠 总结与对比)

🍔 为什么需要工厂模式?

假设你开了一家咖啡店,代码是这样写的:

java 复制代码
public class CoffeeStore {
    public Coffee orderCoffee(String type) {
        Coffee coffee = null;
        if ("americano".equals(type)) {
            coffee = new Americano();
        } else if ("latte".equals(type)) {
            coffee = new Latte();
        }
        // 加糖加奶...
        return coffee;
    }
}

问题来了: 如果现在要加一种卡布奇诺,必须修改 orderCoffee 方法的源码,这违反了开闭原则(对扩展开放,对修改关闭) 。而且,咖啡店不仅负责卖咖啡,还负责生产咖啡,职责不单一。

这时候,我们把生产咖啡的活儿剥离出去,交给专门的工厂来做。

简单工厂模式

简单工厂其实不属于 GoF 23 种设计模式,但它太常用了,可以说是工厂模式的入门。

💡 核心思想

定义一个工厂类,根据传入的参数不同,返回不同的对象实例。

🛠️ 代码实现

java 复制代码
// 1. 定义咖啡接口
public interface Coffee {
    void make();
}
// 2. 具体咖啡实现
public class Americano implements Coffee {
    @Override
    public void make() {
        System.out.println("制作美式咖啡...");
    }
}
public class Latte implements Coffee {
    @Override
    public void make() {
        System.out.println("制作拿铁咖啡...");
    }
}
// 3. 简单工厂类 (核心)
public class SimpleCoffeeFactory {
    public static Coffee createCoffee(String type) {
        if ("americano".equals(type)) {
            return new Americano();
        } else if ("latte".equals(type)) {
            return new Latte();
        } else {
            throw new IllegalArgumentException("没有这种咖啡");
        }
    }
}
// 4. 咖啡店(客户端)
public class CoffeeStore {
    public Coffee orderCoffee(String type) {
        // 咖啡店不自己生产,交给工厂
        Coffee coffee = SimpleCoffeeFactory.createCoffee(type);
        coffee.make();
        return coffee;
    }
}

📊 优缺点分析

  • 优点: 客户端(CoffeeStore)免除了直接创建对象的职责,解除了与具体产品的耦合。
  • 缺点: 违背了开闭原则。每增加一种咖啡,都要修改 SimpleCoffeeFactory 里的 if-else。工厂类的职责过重。

工厂方法模式

为了解决简单工厂违背开闭原则的问题,我们升级一下。

💡 核心思想

定义一个用于创建对象的接口(抽象工厂),让子类决定实例化哪个类。将对象实例化推迟到子类。

🛠️ 代码实现

java 复制代码
// 1. 抽象工厂接口
public interface CoffeeFactory {
    Coffee createCoffee();
}
// 2. 具体工厂实现
public class AmericanoFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new Americano();
    }
}
public class LatteFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new Latte();
    }
}
// 3. 咖啡店(客户端)
public class CoffeeStore {
    // 聚合工厂接口,不再依赖具体工厂
    private CoffeeFactory factory;
    public CoffeeStore(CoffeeFactory factory) {
        this.factory = factory;
    }
    public Coffee orderCoffee() {
        Coffee coffee = factory.createCoffee();
        coffee.make();
        return coffee;
    }
}

客户端调用:

java 复制代码
// 想喝美式,就开一家美式工厂注入进去
CoffeeStore store = new CoffeeStore(new AmericanoFactory());
store.orderCoffee();
// 想喝拿铁,换一家拿铁工厂
store = new CoffeeStore(new LatteFactory());
store.orderCoffee();

📊 优缺点分析

  • 优点: 完美符合开闭原则。新增咖啡种类?只需要新增一个 Coffee 实现类和一个对应的 CoffeeFactory 实现类,完全不用改原有代码!
  • 缺点: 类的数量成倍增加(每个产品对应一个工厂)。在系统复杂度较低时,这种设计显得有些"重"。

抽象工厂模式

如果现在需求又升级了:咖啡店不仅要卖咖啡,还要卖甜点 !而且美式要配提拉米苏,拿铁要配抹茶蛋糕(它们是有组合关系的)。

这时候用工厂方法就需要建一堆工厂,管理起来很乱。抽象工厂登场!

💡 核心思想

提供一个创建一系列相关或相互依赖对象的接口,而不指定它们具体的类。

🛠️ 代码实现

java 复制代码
// 1. 定义甜点接口
public interface Dessert {
    void show();
}
// 2. 具体甜点
public class Tiramisu implements Dessert {
    @Override
    public void show() { System.out.println("意大利提拉米苏"); }
}
public class MatchaCake implements Dessert {
    @Override
    public void show() { System.out.println("抹茶蛋糕"); }
}
// 3. 抽象工厂 (现在是产品族的工厂了)
public interface ItalianDessertFactory {
    Coffee createCoffee();
    Dessert createDessert();
}
// 4. 具体工厂 (美式风味工厂)
public class AmericanStyleFactory implements ItalianDessertFactory {
    @Override
    public Coffee createCoffee() { return new Americano(); }
    @Override
    public Dessert createDessert() { return new Tiramisu(); }
}
// 5. 具体工厂 (拿铁风味工厂)
public class LatteStyleFactory implements ItalianDessertFactory {
    @Override
    public Coffee createCoffee() { return new Latte(); }
    @Override
    public Dessert createDessert() { return new MatchaCake(); }
}

客户端调用:

java 复制代码
// 买一套美式风味套餐
ItalianDessertFactory factory = new AmericanStyleFactory();
Coffee coffee = factory.createCoffee();
Dessert dessert = factory.createDessert();
coffee.make();
dessert.show();

📊 优缺点分析

  • 优点: 当一个产品族(如咖啡+甜点)需要一起使用时,它能保证客户端始终使用同一个族中的对象。
  • 缺点: 规定了所有可能被创建的产品集合,扩展新产品(比如新增"杯子"维度)非常困难,需要修改抽象工厂接口及其所有实现类,严重违背开闭原则。

🧠 总结与对比

别被这三个名字绕晕,记住下面这张表:

模式 核心特征 if-else 在哪? 扩展新产品 扩展新产品族 适用场景
简单工厂 一个具体工厂,生产所有产品 在工厂类里 需改工厂代码 不支持 产品少,逻辑简单
工厂方法 一个抽象工厂,每个产品一个具体工厂 没有了 增加具体工厂即可 不支持 产品种类经常扩展
抽象工厂 一个抽象工厂,生产一个产品族 没有了 非常困难 增加具体工厂即可 固定的产品族体系(如UI换肤)
相关推荐
龙俊杰的读书笔记2 小时前
一文读懂python并发&并行编程--以xinference框架应用为例
开发语言·网络·python
liulilittle2 小时前
递归复制搜索所有的lua文件到指定目录
java·开发语言·lua·cmd
测试19982 小时前
Selenium自动化测试框架的搭建
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
IMPYLH2 小时前
Linux 的 sum 命令
linux·运维·服务器·chrome·python·bash
NE_STOP2 小时前
Redis--Set、ZSet操作命令和benchmark测试工具
java
启山智软2 小时前
前沿主流技术栈商城系统(Java JDK21 + Vue3 + Uniapp)
java·开发语言·uni-app
qq_392690662 小时前
如何处理MongoDB分片集群的连接池耗尽危机_客户端连接与mongos到shard的连接乘数效应
jvm·数据库·python
qq_372154232 小时前
Python异步爬虫如何应对封IP_结合asyncio与代理池实现轮询请求
jvm·数据库·python