当前位置: 首页 > news >正文

设计模式——工厂类设计模式(AI回答)

工厂类设计模式主要思路:不是调用者new真正干活的类,而是实现者在之间加了一个工厂类,实现者的工厂类来new真正干活的类。开发者只要new固定的工厂类?

你的理解非常准确。工厂模式的核心思想就是将“对象的创建”与“对象的使用”完全解耦

让调用者直接new真正干活的类(具体实现类),就像买东西直接去工厂生产线盯着一样,一旦生产线换了(类名改了、构造函数变了),调用者就得大面积修改代码。加了工厂类这个“中间商”,调用者只需要跟中间商打交道即可。

至于你问的“开发者只要 new 固定的工厂类吗?”,这个答案取决于你使用的是哪种“工厂模式”。工厂家族主要分三种,它们面对的场景和new的方式各有不同:

1. 简单工厂模式(Simple Factory)—— 确实是固定的工厂

如果你说的是“固定的工厂类”,通常指的就是简单工厂

  • 怎么做:整个系统只有一个固定的工厂类(比如ShapeFactory)。

  • 怎么用:开发者只需要new这个固定的工厂,或者直接调用它的静态方法,通过传入一个参数(如字符串或枚举)来决定要哪种产品。

  • 代码感觉

    // 开发者只需要和这一个固定工厂打交道 Worker worker = SimpleFactory.createWorker("TypeA");
  • 局限:如果新加了“真正干活的类”,必须要修改这个固定工厂内部的if-elseswitch逻辑,违背了“开闭原则”(对扩展开放,对修改关闭)。

2. 工厂方法模式(Factory Method)—— 工厂本身也抽象了

为了解决简单工厂需要频繁修改工厂类的问题,工厂方法模式把工厂类自己也变成了一个接口或抽象类

  • 怎么做:每一个“真正干活的类”,都有一个与之对应的“专属工厂类”(比如CatWorker对应CatWorkerFactoryDogWorker对应DogWorkerFactory)。

  • 怎么用:这时候开发者不能new一个固定的工厂了,而是要根据业务场景new对应的具体工厂。

  • 代码感觉

    // 想要 A 工人,得 new A 工厂 WorkerFactory factory = new ConcreteWorkerFactoryA(); Worker worker = factory.createWorker();
  • 意义:虽然开发者要new不同的工厂,但由于工厂和产品都被抽象成了接口,后面的业务逻辑依然可以完全不关心具体实现。

3. 抽象工厂模式(Abstract Factory)—— 生产一整套产品族

当你的系统里“真正干活的类”不是孤立的,而是成套出现的(比如:Windows 风格的按钮和 Windows 风格的弹窗是一套;Mac 风格的按钮和 Mac 风格的弹窗是一套)。

  • 怎么做:一个抽象工厂定义了生产一整套产品的方法。

  • 怎么用:开发者在系统初始化时,选择new某一个具体的“套装工厂”(比如MacFactory),之后整个系统拿到的所有组件都是这个套装里的。

  • 代码感觉

    // 一键切换成 Mac 套装工厂 ThemeFactory factory = new MacThemeFactory(); Button btn = factory.createButton(); // 自动获得 Mac 按钮 Window win = factory.createWindow(); // 自动获得 Mac 窗口

总结与现代工程的演变

回到你的问题,你提到的“只new固定的工厂类”在简单工厂里表现最明显。

但在现代的企业级开发(例如使用 Spring 等控制反转/依赖注入框架)中,这个模式被推向了极致:开发者连工厂类都不需要自己去new。框架在底层充当了一个巨大的“终极工厂”,开发者只需要通过注解(如@Autowired@Resource)声明“我需要这个接口”,框架就会自动把真正干活的类或者对应的工厂注入进来,连new工厂的动作都省了。

请详细解释工厂方法模式不用和用的区别

工厂方法模式详解

不用工厂方法时的问题

假设你在写一个游戏,地图上有不同种类的敌人:

// 调用方直接 new 具体类 public class Game { public void spawnEnemy(String type) { if (type.equals("orc")) { Orc orc = new Orc(); orc.init(); orc.spawn(); } else if (type.equals("troll")) { Troll troll = new Troll(); troll.init(); troll.spawn(); } // 新增一种敌人?→ 必须来这里改代码 } }

问题很直接:Game类知道了太多——它知道有Orc、有Troll,还知道怎么初始化每一种。每次新增敌人类型都要翻进来改if/else,违反开闭原则,也让测试变得复杂。


用工厂方法后

第一步:定义产品接口

public interface Enemy { void init(); void spawn(); }

第二步:实现具体产品

public class Orc implements Enemy { @Override public void init() { System.out.println("兽人初始化:装备斧头"); } @Override public void spawn() { System.out.println("兽人出现在战场!"); } } public class Troll implements Enemy { @Override public void init() { System.out.println("巨魔初始化:装备棍棒"); } @Override public void spawn() { System.out.println("巨魔出现在战场!"); } }

第三步:定义抽象工厂(Creator)

public abstract class EnemySpawner { // 工厂方法:声明但不实现,交给子类决定 protected abstract Enemy createEnemy(); // 业务逻辑只面向 Enemy 接口,不知道具体类型 public void spawnEnemy() { Enemy enemy = createEnemy(); // 调工厂方法 enemy.init(); enemy.spawn(); } }

第四步:实现具体工厂(ConcreteCreator)

public class OrcSpawner extends EnemySpawner { @Override protected Enemy createEnemy() { return new Orc(); } } public class TrollSpawner extends EnemySpawner { @Override protected Enemy createEnemy() { return new Troll(); } }

第五步:调用方

public class Game { public void startLevel(EnemySpawner spawner) { // 完全不知道、也不关心是哪种敌人 spawner.spawnEnemy(); } public static void main(String[] args) { Game game = new Game(); game.startLevel(new OrcSpawner()); game.startLevel(new TrollSpawner()); } }

输出:

兽人初始化:装备斧头 兽人出现在战场! 巨魔初始化:装备棍棒 巨魔出现在战场!

现在新增一种龙(Dragon),只需添加两个新类,GameEnemySpawner一行不动:

public class Dragon implements Enemy { @Override public void init() { System.out.println("龙初始化:张开翅膀"); } @Override public void spawn() { System.out.println("龙从天而降!"); } } public class DragonSpawner extends EnemySpawner { @Override protected Enemy createEnemy() { return new Dragon(); } }

对比总结
不用工厂方法用工厂方法
新增产品修改已有if/else只新增两个类
调用方依赖依赖所有具体类只依赖抽象接口
测试难以 mock 具体产品注入任意 mock 工厂
职责创建逻辑散落各处集中在各自工厂类

优缺点

优点:遵循开闭原则;调用方与具体产品解耦;每种产品的创建逻辑内聚在自己的工厂里,便于单独测试和替换。

缺点:类的数量随产品种类线性增长,每增一种产品就多一个工厂类;如果产品种类少且不会变化,这套结构偏重,直接new反而更清晰。

请详细解释抽象工厂模式和工厂方法模式的区别

抽象工厂 vs 工厂方法

一句话区别
  • 工厂方法:一个工厂,生产一种产品
  • 抽象工厂:一个工厂,生产一族产品(多种产品,但风格统一)

用同一个场景来对比

还是游戏场景,但现在敌人不只是一个对象——每种阵营都有自己配套的士兵Boss,而且同一阵营的风格必须统一(不能兽人阵营用了精灵Boss)。


工厂方法的做法

工厂方法只能解决一种产品的创建。要创建士兵和Boss,就得建两套独立的工厂体系

// 第一套:士兵工厂 abstract class SoldierSpawner { protected abstract Soldier createSoldier(); } // 第二套:Boss工厂 abstract class BossSpawner { protected abstract Boss createBoss(); } class OrcSoldierSpawner extends SoldierSpawner { ... } class OrcBossSpawner extends BossSpawner { ... }

问题:两套工厂完全独立,没有任何约束阻止你把OrcSoldierSpawnerTrollBossSpawner混着用——阵营错乱,代码靠自觉维护一致性。


抽象工厂的做法

抽象工厂把"同族产品"的创建收进同一个接口,从结构上保证一致性:

产品接口

public interface Soldier { void attack(); } public interface Boss { void rageAttack(); }

具体产品:兽人阵营

public class OrcSoldier implements Soldier { public void attack() { System.out.println("兽人士兵:挥动斧头"); } } public class OrcBoss implements Boss { public void rageAttack() { System.out.println("兽人Boss:狂暴踩踏"); } }

具体产品:精灵阵营

public class ElfSoldier implements Soldier { public void attack() { System.out.println("精灵士兵:射出箭矢"); } } public class ElfBoss implements Boss { public void rageAttack() { System.out.println("精灵Boss:召唤风暴"); } }

抽象工厂接口——这是核心,把一族产品绑在一起

public interface FactionFactory { Soldier createSoldier(); // 创建该阵营的士兵 Boss createBoss(); // 创建该阵营的Boss }

具体工厂:各阵营实现

public class OrcFactory implements FactionFactory { public Soldier createSoldier() { return new OrcSoldier(); } public Boss createBoss() { return new OrcBoss(); } } public class ElfFactory implements FactionFactory { public Soldier createSoldier() { return new ElfSoldier(); } public Boss createBoss() { return new ElfBoss(); } }

调用方——完全不知道具体阵营

public class Game { private final Soldier soldier; private final Boss boss; // 注入工厂,由工厂保证产品一族一致 public Game(FactionFactory factory) { this.soldier = factory.createSoldier(); this.boss = factory.createBoss(); } public void startBattle() { soldier.attack(); boss.rageAttack(); } public static void main(String[] args) { System.out.println("=== 兽人关卡 ==="); new Game(new OrcFactory()).startBattle(); System.out.println("=== 精灵关卡 ==="); new Game(new ElfFactory()).startBattle(); } }

输出:

=== 兽人关卡 === 兽人士兵:挥动斧头 兽人Boss:狂暴踩踏 === 精灵关卡 === 精灵士兵:射出箭矢 精灵Boss:召唤风暴

新增亡灵阵营?新增UndeadSoldierUndeadBossUndeadFactory三个类,其余代码零修改。


结构对比图
工厂方法 抽象工厂 ───────────────── ────────────────────────── EnemySpawner FactionFactory └─ createEnemy() ├─ createSoldier() └─ createBoss() OrcSpawner OrcFactory └─ createEnemy()→Orc ├─ createSoldier()→OrcSoldier └─ createBoss()→OrcBoss TrollSpawner ElfFactory └─ createEnemy()→Troll ├─ createSoldier()→ElfSoldier └─ createBoss()→ElfBoss 一个工厂方法 = 1×1 一个抽象工厂 = 1×N (1工厂,1产品) (1工厂,N种产品,保证同族)

核心差异总结
工厂方法抽象工厂
解决的问题一种产品的创建解耦一族相关产品的创建解耦
工厂接口方法数1个createXxx()多个createXxx()
产品一致性无约束由工厂接口从结构上保证
扩展新产品种类新增一对类即可需修改工厂接口(破坏性变更)
扩展新产品族新增一个工厂实现即可
适用场景产品种类单一,需要灵活替换产品成族出现,必须保持风格统一

最后一点值得注意:抽象工厂的弱点是横向扩展困难——如果要给所有阵营新增一种产品(比如加"坐骑"),就必须修改FactionFactory接口,所有具体工厂都得跟着改。工厂方法没有这个问题,因为每种产品的工厂体系互相独立。两种模式本质上是在"纵向扩展(加族)"和"横向扩展(加品类)"之间做权衡。

http://www.jsqmd.com/news/1067698/

相关文章:

  • 告别图片加载等待:ImageGlass极速图像查看器全面解析
  • PicklingError: Can‘t pickle <class ‘trl.trainer.sft_config.SFTConfig‘>: it‘s not the same object as
  • Python爬虫实战:政府年度统计公报PDF批量爬取与文本解析系统(附完整代码)
  • 零基础入门GIT
  • 8大主流网盘直链一键获取:告别限速困扰的终极解决方案
  • 如何用JPEXS Free Flash Decompiler拯救你珍贵的Flash数字遗产?
  • 出海企业SSL证书怎么选?Digicert、Sectigo 真实使用体验分享
  • API 是什么?给不懂技术的小白:点菜、快递和“软件服务员”
  • 西方数学历史及关键时间节点和人物
  • 昇腾计算架构CANN图像视觉算子库中ops-cv仓库的目标检测融合算子设计与开放神经网络交换格式插件扩展接入流程及算子自动生成工具使用方法全面技术解读
  • 如何5分钟找出微信单向好友:一键检测谁删除了你的完整指南
  • 技术传输对象的数据封装与传输
  • 从零构建国际宏观数据爬虫:世界银行与IMF数据自动化采集指南
  • 【免费领源码】基于PHP框架的文旅资源展示与智能推荐平台|库存管理/订单发货/车辆管理完整项目
  • AUTOSAR 完全指南:从入门到实践
  • 2026年广东TikTok直播带货课程服务方参照:五家机构定位与能力分析
  • 深度学习中的神经网络设计与优化
  • 论文数据被 Nature 子刊引用是什么体验?谈谈科研数据的 “隐性含金量”
  • MySQL 索引类型选择指南
  • GB/T 4857.17-2017 标准科普|运输包装试验大纲通用规则
  • 云南高原监控工程技术纪实:本土服务商云南凯尊科技全维度解析
  • Gemini 3.5 图表 + 文字混合文档信息融合技术解析:原生多模态架构、统一 Token 序列化与工程实践
  • 多账号浏览器选型:个人多开和团队协作的技术检查清单
  • 什么是涌现?
  • 为什么Redis的SETNX命令可以实现分布式锁?
  • 全域核销领跑全国足浴行业:索易软件四大平台官方直连,数字化实力断层领先
  • 事件驱动化技术事件溯源与命令查询职责分离模式
  • 昇腾计算架构集合通信库的拓扑感知全规约算法实现与多卡分布式训练梯度同步通信调度优化及链路故障自动检测恢复容错机制深度技术解析
  • 升级管理化技术中的升级计划升级实施升级验证
  • 应急管理系统:灾害预警与资源调度的决策支持