设计模式之-工厂方法_工厂设计模式的优点
一、【概念定义】—— “让子类决定造什么”的生产术
工厂方法模式(Factory Method Pattern):
一种“延迟实例化型”创建型设计模式,它定义一个用于创建对象的接口(工厂方法),但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
核心思想:“不直接new,而是调用工厂方法;具体造什么,子类说了算”。
目标:解耦对象创建与使用,支持灵活扩展新产品,符合开闭原则。
口诀:“创建对象我不干,工厂方法来接盘;子类决定造哪个,扩展新品不用改!”
二、【解决的问题】—— “手搓对象”的三大灾难
想象你正在开发一个跨平台UI框架:
- 直接创建:
if (platform == "iOS") button = new iOSButton(); else button = new AndroidButton();
→ 代码中遍布条件判断!新增“Windows按钮”?全局搜索替换! - 工厂方法模式:
- 定义 ButtonFactory.createButton() 抽象方法;
- iOSButtonFactory → 返回 iOSButton;
- AndroidButtonFactory → 返回 AndroidButton;
→ 客户端只需调用 factory.createButton(),具体类型由工厂子类决定!
核心解决:
硬编码创建 → 创建逻辑封装到工厂;扩展困难 → 新增产品?只需新增工厂子类;违反开闭原则 → 工厂方法让系统对扩展开放,对修改关闭!
三、【适用场景】—— “需要灵活创建对象”的四大战场
你该用工厂方法模式,如果符合以下“四需原则”:
场景 | 描述 | 举例 |
对象创建逻辑复杂 | 初始化需多步、依赖配置或环境 | 数据库连接、日志记录器、网络客户端 |
需要解耦创建与使用 | 客户端不想知道具体类名 | UI控件、游戏道具、支付网关 |
支持扩展新产品 | 未来会新增同类产品,且不希望修改现有代码 | 新增支付方式(支付宝、微信)、新增图表类型 |
隐藏具体实现 | 只暴露接口,隐藏具体类(如第三方库) | 框架提供接口,用户实现自己的工厂 |
警告:如果对象创建简单(如 new User()),且无扩展需求 —— 工厂方法是过度设计!
四、【经典案例】—— 四大“流水线王者”的成名战
1. 【跨平台UI控件】—— 一套代码,多端运行!
需要在iOS、Android、Web创建不同风格的按钮
直接new:每个界面文件写满平台判断 → 维护噩梦!
工厂方法:ButtonFactory(抽象工厂);
iOSButtonFactory.createButton() → 返回 iOSButton;
AndroidButtonFactory.createButton() → 返回 AndroidButton;客户端:factory.createButton().render();
效果:新增“WebButton”?只需新增 WebButtonFactory!零修改现有代码!
2. 【支付网关系统】—— 新增支付方式?小菜一碟!
支持支付宝、微信支付,未来要加银联、PayPal
直接new:if(type=="alipay") pay = new Alipay(); → 每次加新支付方式都要改支付模块!
工厂方法:
PaymentFactory.createPayment();AlipayFactory → new Alipay();WeChatPayFactory → new WeChatPay();客户端:factory.createPayment().pay(amount);
效果:新增“PayPal”?只需 PayPalFactory!支付模块纹丝不动!
3. 【游戏道具生成】—— 关卡不同,道具不同!
关卡1掉落“生命药水”,关卡2掉落“魔法卷轴”
直接new:if(level==1) item = new HealthPotion(); → 关卡多了代码爆炸!
工厂方法:ItemFactory.createItem();Level1ItemFactory → new HealthPotion();Level2ItemFactory → new MagicScroll();关卡管理器:factory.createItem().use();
效果:新增关卡3?只需 Level3ItemFactory!关卡逻辑与道具创建彻底解耦!
4. 【数据库连接池】—— 换数据库?配置搞定!
支持MySQL、PostgreSQL,未来可能换Oracle
直接new:conn = new MySQLConnection() → 换数据库?全局替换!
工厂方法:
DBConnectionFactory.createConnection();MySQLFactory → new MySQLConnection();PostgreSQLFactory → new PostgreSQLConnection();客户端:factory.createConnection().query(sql);
效果:换Oracle?只需 OracleFactory + 配置文件切换!代码无需修改!
五、【结构简述】—— 工厂宇宙的“生产四剑客”
工厂方法四要素:
角色 | 职责 | 比喻 |
Product(抽象产品) | 声明产品接口 | “产品蓝图” —— 所有按钮都要有 render() 方法 |
ConcreteProduct(具体产品) | 实现产品接口的具体类 | “具体商品” —— iOS按钮、Android按钮 |
Creator(抽象工厂) | 声明工厂方法(通常抽象),可包含其他业务逻辑 | “工厂蓝图” —— 声明 createButton() 方法 |
ConcreteCreator(具体工厂) | 实现工厂方法,返回具体产品实例 | “具体流水线” —— iOS工厂、Android工厂 |
调用流程:
Client → 持有 ConcreteCreator → 调用 creator.createProduct() → 返回 ConcreteProduct → 使用 product.method()
六、【注意事项】—— “流水线”虽强,小心“过度设计”!
1. 类数量膨胀
每个产品对应一个工厂子类 → 类数量翻倍!
解决:用简单工厂(静态工厂)代替(如果无需扩展);用反射+配置文件动态创建(如 Spring 的 @Bean);权衡:扩展性需求 > 类数量成本 时使用。
2. 客户端需知道具体工厂
客户端必须创建 iOSButtonFactory 或 AndroidButtonFactory → 仍需知道具体类!
解决:用抽象工厂模式封装工厂创建;用依赖注入(如 Spring)自动注入工厂;用配置文件/环境变量决定实例化哪个工厂。
3. 性能开销
多一层工厂方法调用 → 轻微性能损耗(通常可忽略)
权衡:灵活性收益 >> 性能损耗 时使用。
4. 工厂方法 vs 简单工厂
简单工厂:一个工厂类 + 静态方法 + 条件判断 → 不符合开闭原则;工厂方法:多个工厂子类 → 符合开闭原则;
选择:产品稳定、无需扩展 → 简单工厂;产品需扩展、追求开闭 → 工厂方法。
七、【实战应用】—— 30行代码,打造“跨平台按钮工厂”
java深色版本
// 1. 抽象产品
interface Button {
void render();
}
// 2. 具体产品
class iOSButton implements Button {
@Override
public void render() {
System.out.println(" 渲染iOS风格按钮 (圆角+毛玻璃)");
}
}
class AndroidButton implements Button {
@Override
public void render() {
System.out.println(" 渲染Android风格按钮 (Material Design)");
}
}
// 3. 抽象工厂
abstract class ButtonFactory {
// 工厂方法(抽象)
public abstract Button createButton();
// 其他业务逻辑(可选)
public void renderButton() {
Button button = createButton(); // 子类决定创建哪个
button.render();
}
}
// 4. 具体工厂
class iOSButtonFactory extends ButtonFactory {
@Override
public Button createButton() {
return new iOSButton();
}
}
class AndroidButtonFactory extends ButtonFactory {
@Override
public Button createButton() {
return new AndroidButton();
}
}
// 5. 客户端
public class FactoryMethodDemo {
public static void main(String[] args) {
System.out.println(" 跨平台UI框架启动...");
// 模拟:根据配置选择平台
String platform = "iOS"; // 可改为 "Android"
ButtonFactory factory;
if ("iOS".equals(platform)) {
factory = new iOSButtonFactory();
} else {
factory = new AndroidButtonFactory();
}
System.out.println(" 当前平台: " + platform);
factory.renderButton(); // 工厂方法创建并渲染按钮
// 新增平台?只需新增工厂类!无需修改此代码!
}
}运行结果(platform = "iOS"):
深色版本 跨平台UI框架启动...
当前平台: iOS
渲染iOS风格按钮 (圆角+毛玻璃)运行结果(platform = "Android"):
深色版本 跨平台UI框架启动...
当前平台: Android
渲染Android风格按钮 (Material Design)看到了吗?客户端完全不知道具体按钮类!它只和 ButtonFactory 交互!新增“WebButton”?只需:
创建 WebButton implements Button;创建 WebButtonFactory extends ButtonFactory;在配置中选择 WebButtonFactory;
—— 现有代码一行不改!这就是开闭原则的力量!
最后总结:工厂方法模式 —— 对象界的“智能流水线”
以前:对象手搓 → 代码僵化,扩展困难;
现在:工厂流水线 → 子类决定生产什么,扩展新品如搭积木!
工厂方法模式就是对象界的“可扩展生产线” —— 把“造什么”的决定权交给子类,让系统在变化中岿然不动!
记忆口诀:
对象创建太麻烦,
工厂方法来分担。
抽象工厂定接口,
具体工厂造产品。
子类决定实例化,
扩展新品不用改!
跨平台与支付网,
流水线它最划算!
莫为简单对象忙,
需求到位再登场!