设计模式【工厂模式和策略模式】
目录
一、工厂模式
1.1简介
1.2简单工厂模式
1.3工厂方法模式(也是使用最多的)
1.4抽象工厂模式
二、策略模式
2.1简介
2.2结构
2.3客户端如何使用
2.4策略模式的核心优势
三、两种设计模式的对比
3.1直接对比
3.2角色结构对比(结合实例)
3.3代码执行流程对比
3.4使用场景对比
3.5一句话总结区别
一、工厂模式
1.1简介
1.一句话:“不用 new,把‘造什么对象’交给一个专用工厂,客户端只喊名字就能拿到产品。”
2.生活例子——“奶茶店”
产品族:①芝士草莓 ②乌龙奶茶 ③抹茶奶盖。
你只对店员说:“来杯芝士草莓”,店员(工厂)负责怎么把原材料变成饮料。
工厂模式有以下常见的三种
1.2简单工厂模式
简单工厂模式是工厂模式中最基础的形式,通过一个单一的工厂类,根据传入的参数(如类型标识)来决定创建哪种具体产品的实例。
结构:
- 抽象产品(Product):定义所有具体产品的公共接口。
- 具体产品(ConcreteProduct):实现抽象产品接口的具体类。
- 工厂(Factory):包含创建产品的逻辑,根据参数返回对应具体产品实例。
示例代码:假设需要创建不同品牌的汽车(宝马、奔驰):
// 1. 抽象产品:汽车
interface Car {void drive();
}// 2. 具体产品:宝马汽车
class BmwCar implements Car {@Overridepublic void drive() {System.out.println("驾驶宝马汽车");}
}// 2. 具体产品:奔驰汽车
class BenzCar implements Car {@Overridepublic void drive() {System.out.println("驾驶奔驰汽车");}
}// 3. 简单工厂:汽车工厂
class CarFactory {// 根据类型参数创建对应产品public static Car createCar(String type) {if ("bmw".equals(type)) {return new BmwCar();} else if ("benz".equals(type)) {return new BenzCar();} else {throw new IllegalArgumentException("未知车型");}}
}// 使用示例
public class Main {public static void main(String[] args) {// 无需直接new具体产品,通过工厂获取Car bmw = CarFactory.createCar("bmw");bmw.drive(); // 输出:驾驶宝马汽车Car benz = CarFactory.createCar("benz");benz.drive(); // 输出:驾驶奔驰汽车}
}
优缺点:
- 优点:简单直观,调用者无需了解产品创建细节。
- 缺点:违反 “开闭原则”(对扩展开放,对修改关闭),若新增产品(如奥迪),需修改工厂类的
createCar
方法,可能引发风险。
1.3工厂方法模式(也是使用最多的)
为解决简单工厂模式的 “开闭原则” 问题,工厂方法模式将单一工厂拆分为多个具体工厂,每个具体工厂对应一种具体产品的创建。
比如这里的,拿铁工厂生产拿铁,美式生产美式
结构:
- 抽象产品(Product):定义产品的公共接口。
- 具体产品(ConcreteProduct):实现抽象产品接口。
- 抽象工厂(AbstractFactory):定义创建产品的接口(含抽象方法)。
- 具体工厂(ConcreteFactory):实现抽象工厂接口,负责创建对应具体产品。
示例代码:延续汽车案例,为每个品牌创建专属工厂:
// 1. 抽象产品:汽车
interface Car {void drive();
}// 2. 具体产品:宝马汽车
class BmwCar implements Car {@Overridepublic void drive() {System.out.println("驾驶宝马汽车");}
}// 2. 具体产品:奔驰汽车
class BenzCar implements Car {@Overridepublic void drive() {System.out.println("驾驶奔驰汽车");}
}// 3. 抽象工厂:汽车工厂接口
interface CarFactory {Car createCar(); // 抽象创建方法
}// 4. 具体工厂:宝马工厂(只生产宝马)
class BmwFactory implements CarFactory {@Overridepublic Car createCar() {return new BmwCar();}
}// 4. 具体工厂:奔驰工厂(只生产奔驰)
class BenzFactory implements CarFactory {@Overridepublic Car createCar() {return new BenzCar();}
}// 使用示例
public class Main {public static void main(String[] args) {// 通过具体工厂获取产品CarFactory bmwFactory = new BmwFactory();Car bmw = bmwFactory.createCar();bmw.drive(); // 输出:驾驶宝马汽车CarFactory benzFactory = new BenzFactory();Car benz = benzFactory.createCar();benz.drive(); // 输出:驾驶奔驰汽车}
}
优缺点:
- 优点:符合开闭原则,新增产品时只需新增对应的具体产品和具体工厂,无需修改原有代码。
- 缺点:类数量增多(每新增一个产品需新增两个类),复杂度略高。
1.4抽象工厂模式
当需要创建一系列相互关联或依赖的产品族(如 “电脑” 包含 “主机” 和 “显示器”,不同品牌的电脑有对应的主机和显示器)时,抽象工厂模式更适用。
产品族指属于同一品牌(或系列)、相互关联且必须配套使用的一组产品。比如:
- 戴尔电脑的 “戴尔主机 + 戴尔显示器” 是一个产品族(必须配套才能正常工作);
- 苹果电脑的 “苹果主机 + 苹果显示器” 是另一个产品族。抽象工厂模式的目标就是:让客户端能便捷地获取同一产品族的所有产品,且保证它们的兼容性。
结构:
- 抽象产品族(多个抽象产品):如 “主机”“显示器”,每个产品定义接口。
- 具体产品族(多个具体产品):如 “戴尔主机”“戴尔显示器”,对应同一品牌的一系列产品。
- 抽象工厂:定义创建多个抽象产品的接口(含多个抽象方法)。
- 具体工厂:实现抽象工厂接口,负责创建对应品牌的一系列具体产品。
示例代码:
1. 抽象产品(定义产品的 “功能标准”)
是产品族中每个具体产品的通用接口,规定了产品必须实现的功能。比如电脑产品族中有两个抽象产品:
// 抽象产品1:主机(所有主机都必须能“启动”)
interface Host {void start();
}// 抽象产品2:显示器(所有显示器都必须能“显示”)
interface Monitor {void display();
}
2. 具体产品(品牌专属的实际产品)
实现抽象产品接口,是某一品牌下的具体产品,包含该品牌特有的功能实现。比如戴尔和苹果的具体产品:
// 戴尔产品族
class DellHost implements Host {@Overridepublic void start() {System.out.println("戴尔主机启动(戴尔特有的启动逻辑)");}
}
class DellMonitor implements Monitor {@Overridepublic void display() {System.out.println("戴尔显示器显示(戴尔特有的显示效果)");}
}// 苹果产品族
class AppleHost implements Host {@Overridepublic void start() {System.out.println("苹果主机启动(苹果特有的启动逻辑)");}
}
class AppleMonitor implements Monitor {@Overridepublic void display() {System.out.println("苹果显示器显示(苹果特有的显示效果)");}
}
3. 抽象工厂(定义 “生产产品族的标准”)
是一个接口,规定了 “生产某一产品族所需的所有产品” 的方法(即创建每个抽象产品的方法)。比如 “电脑工厂” 必须能生产主机和显示器:
interface ComputerFactory {Host createHost(); // 生产主机(产品族中的一个产品)Monitor createMonitor(); // 生产显示器(产品族中的另一个产品)
}
4. 具体工厂(品牌专属的 “生产线”)
实现抽象工厂接口,是某一品牌的专属工厂,只生产该品牌的产品,保证产品族内的兼容性。比如戴尔工厂只生产戴尔的主机和显示器:
// 戴尔工厂:只生产戴尔产品族
class DellFactory implements ComputerFactory {@Overridepublic Host createHost() {return new DellHost(); // 生产戴尔主机}@Overridepublic Monitor createMonitor() {return new DellMonitor(); // 生产戴尔显示器}
}// 苹果工厂:只生产苹果产品族
class AppleFactory implements ComputerFactory {@Overridepublic Host createHost() {return new AppleHost(); // 生产苹果主机}@Overridepublic Monitor createMonitor() {return new AppleMonitor(); // 生产苹果显示器}
}
客户端只需选择一个品牌工厂,就能获取该品牌的一整套产品,无需关心具体创建细节,且天然保证兼容性:
public class Main {public static void main(String[] args) {// 1. 选择“戴尔工厂”,获取戴尔产品族ComputerFactory dellFactory = new DellFactory();Host dellHost = dellFactory.createHost(); // 戴尔主机(无需手动匹配品牌)Monitor dellMonitor = dellFactory.createMonitor(); // 戴尔显示器(自动配套)dellHost.start(); // 输出:戴尔主机启动(戴尔特有的启动逻辑)dellMonitor.display(); // 输出:戴尔显示器显示(戴尔特有的显示效果)// 2. 切换“苹果工厂”,获取苹果产品族ComputerFactory appleFactory = new AppleFactory();Host appleHost = appleFactory.createHost(); Monitor appleMonitor = appleFactory.createMonitor();appleHost.start(); // 输出:苹果主机启动(苹果特有的启动逻辑)appleMonitor.display(); // 输出:苹果显示器显示(苹果特有的显示效果)}
}
总体示意图如下:
优点:
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
二、策略模式
2.1简介
先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机
作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有很多,可以选择Idea进行开发,也可以使用eclipse进行开发,也可以使用其他的一些开发工具。
策略模式(Strategy Pattern)是一种行为型设计模式,其核心思想是将一组可相互替代的算法或行为封装成独立的策略对象,使得算法可以在运行时动态切换,而不影响使用算法的客户端。这种模式能有效避免代码中大量的if-else
或switch
条件判断,让系统更灵活、易维护。
2.2结构
策略模式的主要角色如下:
抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
1. 抽象策略(Strategy)
定义所有具体策略的公共接口,声明核心执行方法(如 “支付” 操作)。客户端通过这个接口与具体策略交互,无需关心具体实现。
// 抽象策略:支付策略接口
interface PaymentStrategy {void pay(double amount); // 声明支付方法(参数为支付金额)
}
2. 具体策略(ConcreteStrategy)
实现抽象策略接口,封装具体的算法或行为(如不同支付方式的具体流程)。每个具体策略都是独立的,可单独维护和扩展。
// 具体策略1:支付宝支付
class AlipayStrategy implements PaymentStrategy {private String userId; // 支付宝账号public AlipayStrategy(String userId) {this.userId = userId;}@Overridepublic void pay(double amount) {// 支付宝的具体支付逻辑(如调用支付宝接口、验证账号等)System.out.println("用户[" + userId + "]通过支付宝支付了" + amount + "元");}
}// 具体策略2:微信支付
class WechatPayStrategy implements PaymentStrategy {private String openId; // 微信用户标识public WechatPayStrategy(String openId) {this.openId = openId;}@Overridepublic void pay(double amount) {// 微信支付的具体逻辑System.out.println("用户[" + openId + "]通过微信支付了" + amount + "元");}
}// 具体策略3:银行卡支付
class BankCardStrategy implements PaymentStrategy {private String cardNumber; // 卡号private String password; // 密码public BankCardStrategy(String cardNumber, String password) {this.cardNumber = cardNumber;this.password = password;}@Overridepublic void pay(double amount) {// 银行卡支付的具体逻辑(如密码验证、银行接口调用等)System.out.println("使用银行卡[" + cardNumber + "]支付了" + amount + "元(密码验证通过)");}
}
3. 环境类(Context)
持有一个抽象策略的引用,负责与策略对象交互(即调用策略的执行方法)。环境类不实现具体算法,而是将工作委托给策略对象;同时提供方法允许客户端动态更换策略。
// 环境类:支付上下文
class PaymentContext {private PaymentStrategy strategy; // 持有策略引用// 客户端通过此方法设置或切换策略public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;}// 委托策略执行支付(客户端只需调用此方法,无需关心具体支付方式)public void executePayment(double amount) {if (strategy == null) {throw new IllegalStateException("请先选择支付方式");}strategy.pay(amount); // 调用策略的支付方法}
}
2.3客户端如何使用
客户端只需创建具体策略对象,注入到环境类中,即可通过环境类调用策略;若需切换算法,直接更换策略对象即可:
public class Main {public static void main(String[] args) {// 创建环境类PaymentContext context = new PaymentContext();// 选择支付宝支付context.setStrategy(new AlipayStrategy("zhangsan@alipay.com"));context.executePayment(199.9); // 输出:用户[zhangsan@alipay.com]通过支付宝支付了199.9元// 运行时切换为微信支付context.setStrategy(new WechatPayStrategy("wx123456789"));context.executePayment(299.5); // 输出:用户[wx123456789]通过微信支付了299.5元// 切换为银行卡支付context.setStrategy(new BankCardStrategy("6222****1234", "123456"));context.executePayment(599.0); // 输出:使用银行卡[6222****1234]支付了599.0元(密码验证通过)}
}
2.4策略模式的核心优势
-
符合 “开闭原则”新增支付方式(如 “银联支付”)时,只需新增一个实现
PaymentStrategy
的具体策略类(UnionPayStrategy
),无需修改环境类或其他策略,扩展非常方便。 -
消除多重条件判断若不使用策略模式,可能需要用
if-else
判断支付类型:// 无策略模式的弊端:条件判断臃肿,新增支付方式需修改此处 if (type.equals("alipay")) { ... } else if (type.equals("wechat")) { ... } else if (type.equals("bank")) { ... }
策略模式将这些判断转化为 “选择具体策略对象”,代码更清晰、易维护。
-
算法可独立复用每个策略类(如
AlipayStrategy
)可在不同场景中复用(如订单支付、充值等),避免代码重复。 -
动态切换算法运行时可根据用户选择或业务需求(如检测到微信支付故障时自动切换为支付宝)实时更换策略,灵活性极高。
三、两种设计模式的对比
刚开始学这两种设计模式,感觉两种设计模式有很多类似指出,容易弄混,这里做一个区分
工厂模式和策略模式确实有相似之处(比如都使用接口抽象、都通过具体实现类扩展),但它们的核心意图和解决的问题完全不同。简单说:
- 工厂模式是 “创建型模式”,核心是 **“如何创建对象”**,解决的是对象创建的封装问题;
- 策略模式是 “行为型模式”,核心是 **“如何执行行为 / 算法”**,解决的是行为切换的灵活性问题。
3.1直接对比
维度 | 工厂模式(以抽象工厂为例) | 策略模式 |
---|---|---|
核心目的 | 封装对象的创建过程,让客户端无需关心 “如何创建产品”。 | 封装不同的行为 / 算法,让客户端可以灵活切换 “如何执行操作”。 |
解决的问题 | 避免客户端直接通过new 创建对象,隐藏创建细节(如复杂构造、依赖关系)。 | 避免用if-else 硬编码多种行为,实现行为的动态替换。 |
关注焦点 | 对象的 “创建”(new 操作的封装)。 | 对象的 “行为”(方法的执行逻辑)。 |
3.2角色结构对比(结合实例)
1. 工厂模式(抽象工厂:电脑产品族)
角色围绕 “创建产品对象” 设计:
- 抽象产品(Host/Monitor):定义产品的功能(是什么);
- 具体产品(DellHost/AppleMonitor):产品的具体实现;
- 抽象工厂(ComputerFactory):定义创建产品的接口(如何创建);
- 具体工厂(DellFactory/AppleFactory):负责创建具体产品。
核心逻辑:客户端通过 “选择具体工厂”,获取工厂创建的产品对象(如DellFactory
创建DellHost
)。
2. 策略模式(支付方式)
角色围绕 “执行行为 / 算法” 设计:
- 抽象策略(PaymentStrategy):定义行为的接口(做什么);
- 具体策略(AlipayStrategy/WechatPayStrategy):行为的具体实现;
- 环境类(PaymentContext):持有策略引用,委托策略执行行为(如何调用)。
核心逻辑:客户端通过 “选择具体策略”,让环境类执行对应的行为(如AlipayStrategy
执行支付宝支付)。
3.3代码执行流程对比
工厂模式的流程:
“客户端 → 选择工厂 → 工厂创建产品 → 客户端使用产品”
// 选择工厂(决定创建什么产品)
ComputerFactory factory = new DellFactory();
// 工厂创建产品(核心:封装了new操作)
Host host = factory.createHost();
// 客户端使用产品
host.start();
策略模式的流程:
“客户端 → 选择策略 → 环境类委托策略执行行为”
// 创建环境类
PaymentContext context = new PaymentContext();
// 选择策略(决定执行什么行为)
context.setStrategy(new AlipayStrategy("user123"));
// 环境类委托策略执行(核心:封装了行为逻辑)
context.executePayment(100);
3.4使用场景对比
场景示例 | 该用哪种模式? | 原因分析 |
---|---|---|
电商系统创建不同品牌的手机(含机身、电池) | 工厂模式(抽象工厂) | 核心是 “创建一组配套的产品对象”,需隐藏创建细节。 |
电商系统的支付方式(支付宝 / 微信 / 银行卡) | 策略模式 | 核心是 “执行不同的支付行为”,需支持动态切换。 |
游戏中创建不同职业的角色(战士 / 法师的装备) | 工厂模式 | 核心是 “创建角色相关的一套对象”,创建逻辑复杂需封装。 |
游戏中角色的攻击方式(近战 / 远程 / 魔法) | 策略模式 | 核心是 “执行不同的攻击行为”,需根据场景动态切换。 |
3.5一句话总结区别
- 工厂模式:“我给你一个工厂,你拿它创建你要的对象”(关注 “创建什么”);
- 策略模式:“我给你一个策略,你用它执行你要的操作”(关注 “怎么执行”)。
简单说,工厂模式是 “对象的生产工具”,策略模式是 “行为的执行方案”。
感兴趣的宝子可以关注一波,后续会更新更多有用的知识!!!