工厂类设计模式主要思路:不是调用者new真正干活的类,而是实现者在之间加了一个工厂类,实现者的工厂类来new真正干活的类。开发者只要new固定的工厂类?
你的理解非常准确。工厂模式的核心思想就是将“对象的创建”与“对象的使用”完全解耦。
让调用者直接new真正干活的类(具体实现类),就像买东西直接去工厂生产线盯着一样,一旦生产线换了(类名改了、构造函数变了),调用者就得大面积修改代码。加了工厂类这个“中间商”,调用者只需要跟中间商打交道即可。
至于你问的“开发者只要 new 固定的工厂类吗?”,这个答案取决于你使用的是哪种“工厂模式”。工厂家族主要分三种,它们面对的场景和new的方式各有不同:
1. 简单工厂模式(Simple Factory)—— 确实是固定的工厂
如果你说的是“固定的工厂类”,通常指的就是简单工厂。
怎么做:整个系统只有一个固定的工厂类(比如
ShapeFactory)。怎么用:开发者只需要
new这个固定的工厂,或者直接调用它的静态方法,通过传入一个参数(如字符串或枚举)来决定要哪种产品。代码感觉:
// 开发者只需要和这一个固定工厂打交道 Worker worker = SimpleFactory.createWorker("TypeA");局限:如果新加了“真正干活的类”,必须要修改这个固定工厂内部的
if-else或switch逻辑,违背了“开闭原则”(对扩展开放,对修改关闭)。
2. 工厂方法模式(Factory Method)—— 工厂本身也抽象了
为了解决简单工厂需要频繁修改工厂类的问题,工厂方法模式把工厂类自己也变成了一个接口或抽象类。
怎么做:每一个“真正干活的类”,都有一个与之对应的“专属工厂类”(比如
CatWorker对应CatWorkerFactory,DogWorker对应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),只需添加两个新类,Game和EnemySpawner一行不动:
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 { ... }问题:两套工厂完全独立,没有任何约束阻止你把OrcSoldierSpawner和TrollBossSpawner混着用——阵营错乱,代码靠自觉维护一致性。
抽象工厂的做法
抽象工厂把"同族产品"的创建收进同一个接口,从结构上保证一致性:
产品接口
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:召唤风暴新增亡灵阵营?新增UndeadSoldier、UndeadBoss、UndeadFactory三个类,其余代码零修改。
结构对比图
工厂方法 抽象工厂 ───────────────── ────────────────────────── 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接口,所有具体工厂都得跟着改。工厂方法没有这个问题,因为每种产品的工厂体系互相独立。两种模式本质上是在"纵向扩展(加族)"和"横向扩展(加品类)"之间做权衡。