临时插入一个额外知识换换思路,认识一下几个业务中常用的设计模式,尽可能讲明白、多多点赞支持~
引言
在软件开发过程中,设计模式是解决常见问题的经典方案。今天我们将深入探讨三种常用的行为型设计模式:策略模式、责任链模式和模板模式。每种模式都有其独特的适用场景和优势,能够帮助我们编写出更加灵活、可维护的代码。
1. 策略模式 (Strategy Pattern)
模式介绍
策略模式定义了一系列算法,将每个算法封装起来,并且使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。
核心思想
- 封装变化:将经常变化的算法部分抽象出来
- 面向接口:针对接口编程,而不是针对实现
- 组合优于继承:通过组合方式使用算法,避免继承带来的复杂性
适用场景
- 一个系统需要在多种算法中选择一种
- 有多个条件语句定义不同行为的情况
- 希望算法可以自由切换
- 需要隔离算法的使用和实现
经典示例:支付系统
/*** 支付策略接口* 定义所有支付方式必须实现的方法*/
interface PaymentStrategy {/*** 执行支付* @param amount 支付金额* @return 支付结果*/boolean pay(double amount);/*** 获取支付方式名称* @return 支付方式名称*/String getPaymentMethod();
}/*** 支付宝支付策略* 实现支付宝特有的支付逻辑*/
class AlipayStrategy implements PaymentStrategy {@Overridepublic boolean pay(double amount) {System.out.println("使用支付宝支付 " + amount + " 元");// 调用支付宝SDK进行支付System.out.println("调用支付宝接口...");System.out.println("用户确认支付...");System.out.println("支付成功!");return true;}@Overridepublic String getPaymentMethod() {return "支付宝";}
}/*** 微信支付策略* 实现微信支付特有的支付逻辑*/
class WechatPayStrategy implements PaymentStrategy {@Overridepublic boolean pay(double amount) {System.out.println("使用微信支付 " + amount + " 元");// 调用微信支付SDK进行支付System.out.println("调用微信支付接口...");System.out.println("用户输入密码...");System.out.println("支付成功!");return true;}@Overridepublic String getPaymentMethod() {return "微信支付";}
}/*** 银行卡支付策略* 实现银行卡支付特有的支付逻辑*/
class BankCardStrategy implements PaymentStrategy {private String cardNumber;private String cvv;public BankCardStrategy(String cardNumber, String cvv) {this.cardNumber = cardNumber;this.cvv = cvv;}@Overridepublic boolean pay(double amount) {System.out.println("使用银行卡支付 " + amount + " 元");// 调用银行支付网关System.out.println("验证银行卡信息...");System.out.println("卡号: " + maskCardNumber(cardNumber));System.out.println("调用银行支付网关...");System.out.println("支付成功!");return true;}@Overridepublic String getPaymentMethod() {return "银行卡";}private String maskCardNumber(String cardNumber) {// 隐藏银行卡号中间部分if (cardNumber.length() > 8) {return cardNumber.substring(0, 4) + " **** **** " + cardNumber.substring(cardNumber.length() - 4);}return cardNumber;}
}/*** 支付上下文* 负责管理和使用具体的支付策略*/
class PaymentContext {private PaymentStrategy paymentStrategy;/*** 设置支付策略* @param paymentStrategy 具体的支付策略*/public void setPaymentStrategy(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}/*** 执行支付操作* @param amount 支付金额* @return 支付结果*/public boolean executePayment(double amount) {if (paymentStrategy == null) {throw new IllegalStateException("支付策略未设置");}System.out.println("开始" + paymentStrategy.getPaymentMethod() + "支付流程...");return paymentStrategy.pay(amount);}
}/*** 策略模式演示类*/
public class StrategyPatternDemo {public static void main(String[] args) {// 创建支付上下文PaymentContext context = new PaymentContext();// 使用支付宝支付System.out.println("=== 支付宝支付 ===");context.setPaymentStrategy(new AlipayStrategy());context.executePayment(100.0);// 使用微信支付System.out.println("\n=== 微信支付 ===");context.setPaymentStrategy(new WechatPayStrategy());context.executePayment(200.0);// 使用银行卡支付System.out.println("\n=== 银行卡支付 ===");context.setPaymentStrategy(new BankCardStrategy("6225880112345678", "123"));context.executePayment(300.0);}
}
策略模式优势
- 开闭原则:新增支付方式无需修改现有代码
- 消除条件判断:避免大量的if-else语句
- 代码复用:相同算法可以在不同环境中复用
- 易于测试:每个策略都可以独立测试
2. 责任链模式 (Chain of Responsibility Pattern)
模式介绍
责任链模式将请求的发送者和接收者解耦,让多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
核心思想
- 解耦发送者和接收者:发送者不需要知道哪个对象会处理请求
- 动态组合:可以动态地添加或修改处理链
- 灵活处理:每个处理器可以选择处理请求或传递给下一个
适用场景
- 有多个对象可以处理同一个请求
- 想在不明确指定接收者的情况下向多个对象中的一个提交请求
- 需要动态指定一组对象处理请求
经典示例:请假审批系统
/*** 请假请求类* 包含请假的基本信息*/
class LeaveRequest {private String employeeName;private int leaveDays;private String reason;public LeaveRequest(String employeeName, int leaveDays, String reason) {this.employeeName = employeeName;this.leaveDays = leaveDays;this.reason = reason;}// Getter 方法public String getEmployeeName() { return employeeName; }public int getLeaveDays() { return leaveDays; }public String getReason() { return reason; }
}/*** 审批处理器接口* 定义审批处理器的基本行为*/
interface ApprovalHandler {/*** 设置下一个处理器* @param nextHandler 下一个审批处理器*/void setNextHandler(ApprovalHandler nextHandler);/*** 处理请假请求* @param request 请假请求* @return 处理结果*/boolean handleRequest(LeaveRequest request);
}/*** 组长审批处理器* 处理1天以内的请假*/
class GroupLeaderHandler implements ApprovalHandler {private ApprovalHandler nextHandler;@Overridepublic void setNextHandler(ApprovalHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic boolean handleRequest(LeaveRequest request) {System.out.println("组长正在审批 " + request.getEmployeeName() + " 的请假申请...");if (request.getLeaveDays() <= 1) {System.out.println("✅ 组长批准 " + request.getEmployeeName() + " 请假 " + request.getLeaveDays() + " 天,原因:" + request.getReason());return true;}System.out.println("⏭️ 组长无权限审批 " + request.getLeaveDays() + " 天请假,转交上级");return nextHandler != null && nextHandler.handleRequest(request);}
}/*** 班长审批处理器* 处理3天以内的请假*/
class SquadLeaderHandler implements ApprovalHandler {private ApprovalHandler nextHandler;@Overridepublic void setNextHandler(ApprovalHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic boolean handleRequest(LeaveRequest request) {System.out.println("班长正在审批 " + request.getEmployeeName() + " 的请假申请...");if (request.getLeaveDays() <= 3) {System.out.println("✅ 班长批准 " + request.getEmployeeName() + " 请假 " + request.getLeaveDays() + " 天,原因:" + request.getReason());return true;}System.out.println("⏭️ 班长无权限审批 " + request.getLeaveDays() + " 天请假,转交上级");return nextHandler != null && nextHandler.handleRequest(request);}
}/*** 副厂长审批处理器* 处理3天以内的请假(与班长权限相同,但角色不同)*/
class ViceManagerHandler implements ApprovalHandler {private ApprovalHandler nextHandler;@Overridepublic void setNextHandler(ApprovalHandler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic boolean handleRequest(LeaveRequest request) {System.out.println("副厂长正在审批 " + request.getEmployeeName() + " 的请假申请...");if (request.getLeaveDays() <= 3) {System.out.println("✅ 副厂长批准 " + request.getEmployeeName() + " 请假 " + request.getLeaveDays() + " 天,原因:" + request.getReason());return true;}System.out.println("⏭️ 副厂长无权限审批 " + request.getLeaveDays() + " 天请假,转交厂长");return nextHandler != null && nextHandler.handleRequest(request);}
}/*** 厂长审批处理器* 处理所有请假,是责任链的终点*/
class ManagerHandler implements ApprovalHandler {private ApprovalHandler nextHandler;@Overridepublic void setNextHandler(ApprovalHandler nextHandler) {// 厂长是最终审批人,没有下一个处理器}@Overridepublic boolean handleRequest(LeaveRequest request) {System.out.println("厂长正在审批 " + request.getEmployeeName() + " 的请假申请...");if (request.getLeaveDays() <= 10) {System.out.println("✅ 厂长批准 " + request.getEmployeeName() + " 请假 " + request.getLeaveDays() + " 天,原因:" + request.getReason());return true;} else {System.out.println("❌ 厂长拒绝 " + request.getEmployeeName() + " 的请假申请," +"原因:请假天数过长");return false;}}
}/*** 审批链构建器* 负责构建审批责任链*/
class ApprovalChainBuilder {/*** 构建完整的审批责任链* @return 责任链的起始处理器*/public static ApprovalHandler buildChain() {// 创建各个处理器ApprovalHandler groupLeader = new GroupLeaderHandler();ApprovalHandler squadLeader = new SquadLeaderHandler();ApprovalHandler viceManager = new ViceManagerHandler();ApprovalHandler manager = new ManagerHandler();// 构建责任链:组长 -> 班长 -> 副厂长 -> 厂长groupLeader.setNextHandler(squadLeader);squadLeader.setNextHandler(viceManager);viceManager.setNextHandler(manager);return groupLeader;}
}/*** 请假服务类* 提供请假申请服务*/
class LeaveService {private ApprovalHandler approvalHandler;public LeaveService() {this.approvalHandler = ApprovalChainBuilder.buildChain();}/*** 提交请假申请* @param request 请假请求* @return 审批结果*/public boolean submitLeaveRequest(LeaveRequest request) {System.out.println("\n=== " + request.getEmployeeName() + " 提交请假申请 ===");System.out.println("请假天数:" + request.getLeaveDays() + " 天");System.out.println("请假原因:" + request.getReason());return approvalHandler.handleRequest(request);}
}/*** 责任链模式演示类*/
public class ChainOfResponsibilityPatternDemo {public static void main(String[] args) {LeaveService leaveService = new LeaveService();// 测试不同天数的请假申请System.out.println("===== 责任链模式 - 请假审批演示 =====");// 1天请假 - 组长审批LeaveRequest request1 = new LeaveRequest("张三", 1, "身体不适");leaveService.submitLeaveRequest(request1);// 2天请假 - 班长审批LeaveRequest request2 = new LeaveRequest("李四", 2, "家里有事");leaveService.submitLeaveRequest(request2);// 3天请假 - 副厂长审批LeaveRequest request3 = new LeaveRequest("王五", 3, "年假休息");leaveService.submitLeaveRequest(request3);// 5天请假 - 厂长审批LeaveRequest request4 = new LeaveRequest("赵六", 5, "婚假");leaveService.submitLeaveRequest(request4);// 15天请假 - 厂长拒绝LeaveRequest request5 = new LeaveRequest("钱七", 15, "长假旅游");leaveService.submitLeaveRequest(request5);}
}
责任链模式优势
- 降低耦合度:请求发送者无需知道具体的处理者
- 动态配置:可以动态地添加、删除或修改处理链
- 灵活性:可以改变处理链的顺序或结构
- 单一职责:每个处理者只关注自己的处理逻辑
3. 模板模式 (Template Pattern)
模式介绍
模板模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
核心思想
- 封装不变部分:将不变的行为移到父类
- 扩展可变部分:将变化的行为由子类实现
- 代码复用:公共的代码在父类中实现
- 控制子类扩展:通过模板方法控制子类的扩展点
适用场景
- 有多个类包含相同的操作,但其中某些步骤的实现不同
- 需要控制子类扩展时,只允许在特定点进行扩展
- 重要的复杂算法需要定义骨架,细节由子类实现
经典示例:饮料制作系统
/*** 抽象饮料类 - 模板类* 定义饮料制作的基本流程*/
abstract class BeverageTemplate {/*** 模板方法 - 定义饮料制作的完整流程(final防止子类重写)*/public final void prepareBeverage() {boilWater(); // 煮沸水brew(); // 冲泡(由子类实现)pourInCup(); // 倒入杯子addCondiments(); // 添加调料(由子类实现)// 钩子方法 - 子类可以选择性重写if (customerWantsCondiments()) {extraStep(); // 额外步骤}serve(); // 上饮料}/*** 煮沸水 - 具体方法(所有饮料都需要煮沸水)*/private void boilWater() {System.out.println("1. 🚰 煮沸水");}/*** 倒入杯子 - 具体方法(所有饮料都需要倒入杯子)*/private void pourInCup() {System.out.println("3. 🥤 倒入杯子");}/*** 上饮料 - 具体方法(所有饮料都需要上桌)*/private void serve() {System.out.println("6. 🍹 上饮料:" + getBeverageName());}/*** 钩子方法 - 客户是否想要调料(默认返回true,子类可重写)*/protected boolean customerWantsCondiments() {return true;}/*** 额外步骤 - 钩子方法(默认空实现,子类可重写)*/protected void extraStep() {// 默认不执行额外步骤}/*** 冲泡方法 - 抽象方法(必须由子类实现)*/protected abstract void brew();/*** 添加调料 - 抽象方法(必须由子类实现)*/protected abstract void addCondiments();/*** 获取饮料名称 - 抽象方法(必须由子类实现)*/protected abstract String getBeverageName();
}/*** 茶类 - 具体模板* 实现茶的具体制作步骤*/
class Tea extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("2. 🍃 冲泡茶叶");}@Overrideprotected void addCondiments() {System.out.println("4. 🍋 加入柠檬");}@Overrideprotected String getBeverageName() {return "柠檬茶";}@Overrideprotected void extraStep() {System.out.println("5. ⏳ 静置2分钟让茶味更浓郁");}
}/*** 咖啡类 - 具体模板* 实现咖啡的具体制作步骤*/
class Coffee extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("2. ☕ 冲泡咖啡粉");}@Overrideprotected void addCondiments() {System.out.println("4. 🥛 加入糖和牛奶");}@Overrideprotected String getBeverageName() {return "拿铁咖啡";}@Overrideprotected boolean customerWantsCondiments() {// 假设有30%的顾客不要调料return Math.random() > 0.3;}
}/*** 纯茶类 - 具体模板* 实现不加调料的茶*/
class PureTea extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("2. 🍃 冲泡茶叶");}@Overrideprotected void addCondiments() {// 纯茶不加任何调料System.out.println("4. ⏩ 纯茶,跳过添加调料");}@Overrideprotected String getBeverageName() {return "纯茶";}@Overrideprotected boolean customerWantsCondiments() {// 纯茶永远不加调料return false;}
}/*** 饮料店类* 提供制作饮料的服务*/
class BeverageShop {/*** 制作指定类型的饮料* @param beverage 饮料模板*/public void makeBeverage(BeverageTemplate beverage) {System.out.println("\n=== 开始制作饮料 ===");beverage.prepareBeverage();System.out.println("=== 饮料制作完成 ===\n");}
}/*** 模板模式演示类*/
public class TemplatePatternDemo {public static void main(String[] args) {BeverageShop shop = new BeverageShop();System.out.println("===== 模板模式 - 饮料制作演示 =====");// 制作茶shop.makeBeverage(new Tea());// 制作咖啡(可能不加调料)shop.makeBeverage(new Coffee());// 制作纯茶(不加调料)shop.makeBeverage(new PureTea());// 演示多次咖啡制作(展示钩子方法的效果)System.out.println("多次制作咖啡(展示钩子方法效果):");for (int i = 0; i < 3; i++) {System.out.println("\n第 " + (i + 1) + " 次:");shop.makeBeverage(new Coffee());}}
}
模板模式优势
- 代码复用:公共代码在父类中实现,避免代码重复
- 提高扩展性:子类可以通过实现抽象方法来扩展算法的特定步骤
- 控制子类:父类可以控制算法的结构,防止子类修改算法骨架
- 符合开闭原则:对扩展开放,对修改关闭
三种模式对比
| 特性 | 策略模式 | 责任链模式 | 模板模式 |
|---|---|---|---|
| 主要目的 | 封装算法,使其可互换 | 解耦发送者和接收者 | 定义算法骨架,子类实现细节 |
| 关注点 | 算法选择 | 请求传递 | 算法结构 |
| 灵活性 | 运行时切换策略 | 动态构建处理链 | 编译时确定结构 |
| 适用场景 | 多种算法选择 | 多级审批、过滤 | 固定流程,可变实现 |
| 扩展方式 | 新增策略类 | 新增处理器 | 新增子类 |
总结
设计模式是软件开发中宝贵的经验总结,正确使用设计模式可以显著提高代码的质量和可维护性。
- 策略模式适合处理算法选择的场景,让算法可以独立于客户端变化
- 责任链模式适合处理请求需要经过多个处理环节的场景,实现发送者和接收者的解耦
- 模板模式适合处理有固定流程但某些步骤需要不同实现的场景,实现代码复用和扩展控制
在实际项目中,我们应该根据具体业务需求选择合适的设计模式,有时甚至需要组合使用多种模式来解决复杂的问题。掌握这些设计模式的精髓,能够帮助我们成为更优秀的软件工程师。