Java 设计模式——工厂模式:从原理到实战的系统指南

Java 设计模式------工厂模式:从原理到实战的系统指南

工厂模式是创建型设计模式的基础,核心思想是将对象的创建逻辑与使用逻辑分离 ,通过工厂类统一封装对象实例化过程,避免直接使用new操作。它虽结构简单,但在降低代码耦合、提升可维护性上作用显著,是企业级开发中高频使用的模式之一。

文章目录

一、核心原理:为什么需要工厂模式?

1. 传统对象创建的痛点

直接通过new关键字创建对象,会导致 "创建逻辑" 与 "使用逻辑" 强耦合,存在以下问题:

  • 耦合度高:使用方需了解对象的创建细节(如构造参数、依赖关系),若对象创建逻辑修改(如新增参数),所有使用处需同步修改;

  • 代码冗余:多个地方创建同一类型对象时,重复编写创建逻辑,违反 "单一职责原则";

  • 扩展性差 :新增同类对象(如新增手机品牌)时,需在所有使用处添加if-else判断,违反 "开闭原则"。

以手机对象创建为例,传统写法的问题如下:

java 复制代码
// 直接new对象,耦合创建逻辑
Phone phone;
if (name.equals("华为")) {
   phone = new HuaWeiPhoneImpl(4, 21); // 需知道构造参数含义
} else if (name.equals("小米")) {
   phone = new XiaoMiPhoneImpl(4, 21);
} else {
   phone = null;
}

2. 工厂模式的解决方案

工厂模式通过引入 "工厂类" 作为中间层,承担对象创建的职责,实现:

  1. 解耦:使用方仅依赖工厂接口,无需关心对象创建细节;

  2. 复用:创建逻辑集中在工厂,避免代码冗余;

  3. 扩展:新增对象类型时,仅需扩展工厂,无需修改现有代码。

二、工厂模式的三种实现方式

根据复杂度和扩展性,工厂模式分为简单工厂模式工厂方法模式抽象工厂模式,三者适用场景逐步递进。

1. 简单工厂模式(静态工厂):单一工厂生产所有产品

核心逻辑

通过一个静态工厂类,根据输入参数(如产品类型)动态创建对应产品对象,无需创建工厂实例。

实战案例:手机生产工厂
(1)产品接口与实现类
java 复制代码
// 手机接口(产品抽象)
public interface Phone {
   String callUp(); // 核心功能:打电话
}

// 华为手机实现类(具体产品)
public class HuaWeiPhoneImpl implements Phone {
   private Integer cpuCount;
   private Integer memoryStorage;

   // 构造函数(封装创建细节)
   public HuaWeiPhoneImpl(Integer cpuCount, Integer memoryStorage) {
       this.cpuCount = cpuCount;
       this.memoryStorage = memoryStorage;
   }

   @Override
   public String callUp() {
       return "华为手机打电话(CPU:" + cpuCount + "核,内存:" + memoryStorage + "GB)";
   }
}

// 小米手机实现类(具体产品)
public class XiaoMiPhoneImpl implements Phone {
   private Integer cpuCount;
   private Integer memoryStorage;

   public XiaoMiPhoneImpl(Integer cpuCount, Integer memoryStorage) {
       this.cpuCount = cpuCount;
       this.memoryStorage = memoryStorage;
   }

   @Override
   public String callUp() {
       return "小米手机打电话(CPU:" + cpuCount + "核,内存:" + memoryStorage + "GB)";
   }
}
(2)简单工厂类
java 复制代码
// 手机静态工厂(封装所有产品的创建逻辑)
public class PhoneFactory {
   // 静态方法:根据品牌参数创建对应手机
   public static Phone createPhone(String brand) {
       switch (brand) {
           case "华为":
               return new HuaWeiPhoneImpl(8, 256); // 固定创建参数,隐藏细节
           case "小米":
               return new XiaoMiPhoneImpl(8, 256);
           default:
               throw new IllegalArgumentException("不支持的手机品牌:" + brand);
       }
   }
}
(3)使用方调用
java 复制代码
// 控制器(使用方,无需关心创建细节)
@RestController
@RequestMapping("product")
public class ProductController {
   @GetMapping("phone")
   public String getPhone(String brand) {
       // 仅调用工厂方法,无需new对象
       Phone phone = PhoneFactory.createPhone(brand);
       return phone.callUp();
   }
}
(4)测试结果

调用/product/phone?brand=华为,返回:

text 复制代码
华为手机打电话(CPU:8核,内存:256GB)
优缺点与适用场景
  • 优点:实现简单,无需创建工厂实例,适合快速开发;

  • 缺点 :工厂类与产品类型强耦合,新增产品需修改工厂switch逻辑,违反 "开闭原则";

  • 适用场景:产品类型少、变化少的简单场景(如工具类创建、配置对象生成)。

2. 工厂方法模式:一个产品对应一个工厂

核心逻辑

定义 "工厂接口",每个具体产品对应一个 "具体工厂",通过工厂接口的多态性实现产品创建,解决简单工厂的扩展问题。

实战案例:手机品牌工厂
(1)工厂接口与具体工厂
java 复制代码
// 手机工厂接口(工厂抽象)
public interface PhoneFactory {
   Phone createPhone(); // 工厂方法:创建对应品牌手机
}

// 华为手机工厂(具体工厂)
public class HuaWeiFactoryImpl implements PhoneFactory {
   @Override
   public Phone createPhone() {
       // 封装华为手机的创建细节
       return new HuaWeiPhoneImpl(8, 256);
   }
}

// 小米手机工厂(具体工厂)
public class XiaoMiFactoryImpl implements PhoneFactory {
   @Override
   public Phone createPhone() {
       return new XiaoMiPhoneImpl(8, 256);
   }
}
(2)使用方调用(结合工厂选择逻辑)
java 复制代码
@RestController
@RequestMapping("product")
public class ProductController {
   @GetMapping("phone")
   public String getPhone(String brand) {
       // 根据品牌选择对应工厂(可抽离为工厂选择器,避免if-else冗余)
       PhoneFactory factory;
       if (brand.equals("华为")) {
           factory = new HuaWeiFactoryImpl();
       } else if (brand.equals("小米")) {
           factory = new XiaoMiFactoryImpl();
       } else {
           throw new IllegalArgumentException("不支持的手机品牌:" + brand);
       }

       // 工厂创建产品
       Phone phone = factory.createPhone();
       return phone.callUp();
   }
}
优缺点与适用场景
  • 优点:新增产品时,仅需新增 "具体产品类" 和 "具体工厂类",无需修改现有代码,符合 "开闭原则";

  • 缺点:产品与工厂一一对应,产品类型过多时会导致 "工厂泛滥",增加代码复杂度;

  • 适用场景:产品类型较多但单一维度扩展(如仅新增品牌,不新增产品品类)的场景。

3. 抽象工厂模式:一个工厂生产多个关联产品

核心逻辑

定义 "抽象工厂接口",每个具体工厂负责生产一组关联产品(如同一品牌的手机、路由器),解决多品类产品的统一创建问题。

实战案例:品牌产品族工厂
(1)新增关联产品接口(路由器)
java 复制代码
// 路由器接口(关联产品抽象)
public interface Router {
   String receiveData(); // 核心功能:接收网络数据
}

// 华为路由器实现类(具体关联产品)
public class HuaWeiRouterImpl implements Router {
   private Integer networkSpeed; // 网速(Mbps)

   public HuaWeiRouterImpl(Integer networkSpeed) {
       this.networkSpeed = networkSpeed;
   }

   @Override
   public String receiveData() {
       return "华为路由器接收数据(网速:" + networkSpeed + "Mbps)";
   }
}

// 小米路由器实现类(具体关联产品)
public class XiaoMiRouterImpl implements Router {
   private Integer networkSpeed;

   public XiaoMiRouterImpl(Integer networkSpeed) {
       this.networkSpeed = networkSpeed;
   }

   @Override
   public String receiveData() {
       return "小米路由器接收数据(网速:" + networkSpeed + "Mbps)";
   }
}
(2)抽象工厂接口与具体工厂
java 复制代码
// 产品族工厂接口(抽象工厂,生产一组关联产品)
public interface BrandFactory {
   Phone createPhone();     // 生产手机
    
   Router createRouter();   // 生产路由器
}

// 华为产品族工厂(具体工厂,生产华为的所有产品)
public class HuaWeiBrandFactory implements BrandFactory {
   @Override
   public Phone createPhone() {
       return new HuaWeiPhoneImpl(8, 256);
   }

   @Override
   public Router createRouter() {
       return new HuaWeiRouterImpl(3000); // 华为路由器网速3000Mbps
   }
}

// 小米产品族工厂(具体工厂,生产小米的所有产品)
public class XiaoMiBrandFactory implements BrandFactory {
   @Override
   public Phone createPhone() {
       return new XiaoMiPhoneImpl(8, 256);
   }

   @Override
   public Router createRouter() {
       return new XiaoMiRouterImpl(2500); // 小米路由器网速2500Mbps
   }
}
(3)使用方调用
java 复制代码
@RestController
@RequestMapping("product")
public class ProductController {
   // 获取品牌的手机
   @GetMapping("phone")
   public String getPhone(String brand) {
       BrandFactory factory = getBrandFactory(brand);
       Phone phone = factory.createPhone();
       return phone.callUp();
   }

   // 获取品牌的路由器
   @GetMapping("router")
   public String getRouter(String brand) {
       BrandFactory factory = getBrandFactory(brand);
       Router router = factory.createRouter();
       return router.receiveData();
   }

   // 抽取工厂选择逻辑,避免代码冗余
   private BrandFactory getBrandFactory(String brand) {
       switch (brand) {
           case "华为":
               return new HuaWeiBrandFactory();
           case "小米":
               return new XiaoMiBrandFactory();
           default:
               throw new IllegalArgumentException("不支持的品牌:" + brand);
       }
   }
}
(4)测试结果

调用/product/router?brand=华为,返回:

text 复制代码
华为路由器接收数据(网速:3000Mbps)
优缺点与适用场景
  • 优点:统一管理同一产品族的创建,确保产品间的兼容性;新增产品族时,仅需新增具体工厂,扩展性强;

  • 缺点:新增产品品类(如新增 "智能手表")时,需修改抽象工厂接口及所有具体工厂,违反 "开闭原则";

  • 适用场景:多品类、多品牌的产品体系(如电子设备、家居产品),且产品族内关联紧密的场景。

三、三种工厂模式的对比与选型

对比维度 简单工厂模式 工厂方法模式 抽象工厂模式
核心思想 单一工厂生产所有产品 一个产品对应一个工厂 一个工厂生产一组关联产品
工厂数量 1 个(静态工厂) N 个(与产品数量一致) M 个(与产品族数量一致)
扩展性(新增产品) 需修改工厂逻辑,违反开闭原则 新增产品 + 工厂,符合开闭原则 需修改抽象工厂,违反开闭原则
代码复杂度
适用场景 产品少、变化少 产品多、单一品类扩展 多品类、产品族关联紧密
典型案例 工具类创建、配置对象 单一品类多品牌(如手机) 多品类产品族(如电子设备)

选型建议

  1. 快速开发 / 简单场景:优先用简单工厂模式(如工具类、配置类);

  2. 单一品类扩展:用工厂方法模式(如仅新增手机品牌,不新增品类);

  3. 多品类产品族:用抽象工厂模式(如电子设备品牌,包含手机、路由器、手表)。

四、工厂模式的框架应用与实战技巧

1. 框架中的工厂模式

(1)Spring 中的工厂模式
  • BeanFactory :简单工厂模式的体现,通过getBean()方法根据 Bean 名称 / 类型创建 Bean 对象;

  • FactoryBean :工厂方法模式的体现,每个FactoryBean负责创建特定类型的 Bean(如SqlSessionFactoryBean创建SqlSessionFactory);

  • ApplicationContext:抽象工厂模式的扩展,管理 Bean 的生命周期,同时提供事件发布、资源加载等多维度功能。

(2)JDK 中的工厂模式
  • Calendar.getInstance() :简单工厂模式,根据时区 / 地区创建不同的Calendar实例;

  • Connection :抽象工厂模式,DriverManager根据 URL 创建不同数据库的Connection实例(如 MySQL、Oracle)。

2. 实战技巧

(1)消除工厂选择的if-else

通过 "工厂映射表" 替代if-else,提升扩展性:

java 复制代码
// 工厂映射表(初始化时注册所有工厂)
private Map<String, BrandFactory> factoryMap;

// 初始化工厂映射
@PostConstruct
public void initFactoryMap() {
   factoryMap = new HashMap<>();
   factoryMap.put("华为", new HuaWeiBrandFactory());
   factoryMap.put("小米", new XiaoMiBrandFactory());
}

// 通过映射表获取工厂,无if-else
private BrandFactory getBrandFactory(String brand) {
   BrandFactory factory = factoryMap.get(brand);
   if (factory == null) {
       throw new IllegalArgumentException("不支持的品牌:" + brand);
   }
   return factory;
}
(2)工厂的单例化

具体工厂类通常无状态,可通过单例模式避免重复创建:

java 复制代码
// 华为工厂单例
public class HuaWeiBrandFactory implements BrandFactory {
   // 私有构造函数
   private HuaWeiBrandFactory() {}

   // 静态内部类实现单例
   private static class SingletonHolder {
       private static final HuaWeiBrandFactory INSTANCE = new HuaWeiBrandFactory();
   }

   public static HuaWeiBrandFactory getInstance() {
       return SingletonHolder.INSTANCE;
   }
   
   // 工厂方法...
}
(3)产品创建的复杂逻辑封装

若产品创建需依赖其他服务(如数据库查询、配置加载),可在工厂中注入依赖:

java 复制代码
// 依赖注入的工厂(Spring环境)
@Service
public class HuaWeiBrandFactory implements BrandFactory {
   // 注入配置服务
   @Autowired
   private DeviceConfigService configService;

   @Override
   public Phone createPhone() {
       // 从配置服务获取参数,而非硬编码
       DeviceConfig config = configService.getConfig("huawei.phone");
       return new HuaWeiPhoneImpl(config.getCpuCount(), config.getMemoryStorage());
   }
   
   // ...
}

五、避坑指南

  1. 避免过度设计:简单场景用简单工厂即可,无需强行使用工厂方法或抽象工厂,增加不必要的复杂度;

  2. 工厂职责单一:工厂仅负责对象创建,不包含业务逻辑(如产品的使用、数据处理),符合 "单一职责原则";

  3. 注意线程安全:若工厂中存在共享状态(如缓存、计数器),需添加同步锁或使用线程安全的容器;

  4. Spring 环境下优先用依赖注入 :无需手动创建工厂实例,通过@Autowired注入工厂,利用 Spring 管理生命周期。

六、总结

工厂模式的核心是 "分离创建与使用",三种实现方式分别对应不同复杂度的场景:

  • 简单工厂模式是 "入门款",适合快速落地;

  • 工厂方法模式是 "进阶款",适合单一品类扩展;

  • 抽象工厂模式是 "高级款",适合多品类产品族。

在实际开发中,需根据产品复杂度、扩展需求选择合适的模式,同时结合框架特性(如 Spring 的依赖注入)简化实现,避免过度设计。掌握工厂模式,不仅能提升代码的可维护性,更能理解框架底层的设计思想(如 Spring、JDK 的工厂应用),为架构设计打下基础。

相关推荐
爱学习的uu3 小时前
CURSOR最新使用指南及使用思路
人工智能·笔记·python·软件工程
叶凡要飞3 小时前
RTX5060Ti安装双系统ubuntu22.04各种踩坑点(黑屏,引导区修复、装驱动、server版本安装)
人工智能·python·yolo·ubuntu·机器学习·操作系统
懒羊羊不懒@3 小时前
Java基础语法—最小单位、及注释
java·c语言·开发语言·数据结构·学习·算法
yuluo_YX3 小时前
VSR 项目解析
人工智能·python
ss2733 小时前
手写Spring第4弹: Spring框架进化论:15年技术变迁:从XML配置到响应式编程的演进之路
xml·java·开发语言·后端·spring
DokiDoki之父3 小时前
MyBatis—增删查改操作
java·spring boot·mybatis
兩尛4 小时前
Spring面试
java·spring·面试
Java中文社群4 小时前
服务器被攻击!原因竟然是他?真没想到...
java·后端
计算衎4 小时前
python通过win32com库调用UDE工具来做开发调试实现自动化源码,以及UDE的知识点介绍
python·c/c++·pywin32·ude·com api