前言
从第一篇工厂模式开始,我会持续地更新每一种设计模式的内容,争取用通俗易懂的语言讲解和解释清楚。如果对你学习设计模式有帮助,请不要吝啬手中的赞~ 如果对文章内容有任何疑惑都可以在评论区提出和讨论~
本系列文章中的完整源码已上传 github 仓库,你可以在这里 github.com/FatMii/Desi…获取。
同样的,如果对你有帮助,请给我一个star~谢谢
设计模式合集链接:
Hello~大家好,本篇文章我们继续学习设计模式第六篇:策略模式模式
一、介绍
策略模式是一种行为型设计模式,其核心在于将一系列算法封装起来,使它们可以互换使用。这样做的好处是,你可以根据不同的需求灵活选择算法,而无需每次都重新编写代码。
策略模式主要包含以下三个关键部分:
- 策略接口(Strategy) :这是一个通用的接口,定义了所有算法的公共操作。所有具体的算法都必须实现这个接口,从而保证它们可以被统一调用。
- 具体策略(ConcreteStrategy) :这些是实现了策略接口的具体算法。每个具体策略都提供了一种特定的实现方式,用于完成特定的任务。
- 上下文(Context) :这是一个封装类,它持有一个策略接口的引用,并通过这个引用来调用具体的算法。上下文类负责根据不同的需求选择合适的策略,并委托其执行任务。它并不关心具体策略的实现细节,只关注策略接口的定义。
通过这种设计,策略模式能够将算法的定义与使用分离开来,使得算法可以独立于客户端变化,从而提高了系统的灵活性和可扩展性。
二、前端场景:表单规则校验
在前端开发中,表单校验是一个非常常见的功能。我们经常需要对用户的输入进行各种校验,比如用户名不能为空、密码长度必须大于6位、邮箱格式是否正确等等。如果用传统的 if-else 或 switch-case 来实现,代码很快就会变得像一团乱麻,维护起来比爬山还累。
1. 传统方式:噩梦般的代码
假设我们有一个用户注册表单,需要校验用户名、密码和邮箱。传统的代码可能长这样:
function validateForm(form) {
if (!form.username) {
console.error("用户名不能为空!");
return false;
}
if (form.username.length < 3) {
console.error("用户名长度不能小于3位!");
return false;
}
if (!form.password) {
console.error("密码不能为空!");
return false;
}
if (form.password.length < 6) {
console.error("密码长度不能小于6位!");
return false;
}
if (!form.email) {
console.error("邮箱不能为空!");
return false;
}
if (!/^\w+@\w+.\w+$/.test(form.email)) {
console.error("邮箱格式不正确!");
return false;
}
console.log("表单校验通过!");
return true;
}
看起来还不错,但如果以后要增加新的校验规则,比如手机号校验,就需要修改这个函数,添加一个新的 if 条件。这样代码就会越来越臃肿,维护起来也很麻烦。
2. 策略模式:优雅的“百宝箱”
现在,让我们用策略模式来改造它,让表单校验变成一个灵活的“百宝箱”:
定义策略接口
class ValidationStrategy {
validate(value) {
throw new Error("校验方法未实现!");
}
}
实现具体策略
class UsernameValidationStrategy extends ValidationStrategy {
validate(value) {
if (!value) {
throw new Error("用户名不能为空!");
}
if (value.length < 3) {
throw new Error("用户名长度不能小于3位!");
}
return true;
}
}
class PasswordValidationStrategy extends ValidationStrategy {
validate(value) {
if (!value) {
throw new Error("密码不能为空!");
}
if (value.length < 6) {
throw new Error("密码长度不能小于6位!");
}
return true;
}
}
class EmailValidationStrategy extends ValidationStrategy {
validate(value) {
if (!value) {
throw new Error("邮箱不能为空!");
}
if (!/^\w+@\w+.\w+$/.test(value)) {
throw new Error("邮箱格式不正确!");
}
return true;
}
}
创建上下文
class FormValidator {
constructor() {
this.strategies = {};
}
addStrategy(field, strategy) {
this.strategies[field] = strategy;
}
validate(form) {
for (const field in form) {
if (this.strategies[field]) {
try {
this.strategies[field].validate(form[field]);
} catch (error) {
console.error(`字段 ${field} 校验失败:${error.message}`);
return false;
}
}
}
console.log("表单校验通过!");
return true;
}
}
使用策略模式
// 创建具体策略
const usernameStrategy = new UsernameValidationStrategy();
const passwordStrategy = new PasswordValidationStrategy();
const emailStrategy = new EmailValidationStrategy();
// 创建表单校验器并添加策略
const formValidator = new FormValidator();
formValidator.addStrategy("username", usernameStrategy);
formValidator.addStrategy("password", passwordStrategy);
formValidator.addStrategy("email", emailStrategy);
// 测试表单校验
const form = {
username: "kimi",
password: "123456",
email: "kimi@moonshot.cn"
};
formValidator.validate(form); // 输出:表单校验通过!
// 如果某个字段不符合规则
const invalidForm = {
username: "ki", // 用户名长度小于3位
password: "123456",
email: "kimi@moonshot.cn"
};
formValidator.validate(invalidForm); // 输出:字段 username 校验失败:用户名长度不能小于3位!
看,我们的表单校验功能现在变得多么灵活!新增校验规则时,只需要添加一个新的策略类,完全不需要修改现有的代码。这就是策略模式的魅力所在,它让算法的切换变得像换衣服一样简单。
三、后端场景:订单处理
在后端开发中,订单处理是一个非常复杂的业务逻辑。不同的订单类型(如普通订单、会员订单、团购订单等)可能需要不同的处理流程。如果用传统的 if-else 或 switch-case 来实现,代码很快就会变得像一团乱麻,维护起来比爬山还累。
1. 传统方式:噩梦般的代码
假设我们有一个订单处理系统,需要根据订单类型选择不同的处理逻辑。传统的代码可能长这样:
public class OrderProcessor {
public void process(Order order) {
if ("normal".equals(order.getType())) {
processNormalOrder(order);
} else if ("vip".equals(order.getType())) {
processVipOrder(order);
} else if ("group".equals(order.getType())) {
processGroupOrder(order);
} else {
System.out.println("不支持的订单类型!");
}
}
private void processNormalOrder(Order order) {
System.out.println("处理普通订单:" + order.getId());
// 普通订单处理逻辑
}
private void processVipOrder(Order order) {
System.out.println("处理会员订单:" + order.getId());
// 会员订单处理逻辑
}
private void processGroupOrder(Order order) {
System.out.println("处理团购订单:" + order.getId());
// 团购订单处理逻辑
}
}
看起来还不错,但如果以后要增加新的订单类型,比如限时抢购订单,就需要修改 process 方法,添加一个新的 else if。这样代码就会越来越臃肿,维护起来也很麻烦。
2. 策略模式改造
现在,让我们用策略模式来改造它,让订单处理变成一个灵活的“变形金刚”:
定义策略接口
public interface OrderProcessingStrategy {
void process(Order order);
}
实现具体策略
public class NormalOrderProcessingStrategy implements OrderProcessingStrategy {
@Override
public void process(Order order) {
System.out.println("处理普通订单:" + order.getId());
// 普通订单处理逻辑
}
}
public class VipOrderProcessingStrategy implements OrderProcessingStrategy {
@Override
public void process(Order order) {
System.out.println("处理会员订单:" + order.getId());
// 会员订单处理逻辑
}
}
public class GroupOrderProcessingStrategy implements OrderProcessingStrategy {
@Override
public void process(Order order) {
System.out.println("处理团购订单:" + order.getId());
// 团购订单处理逻辑
}
}
创建上下文
public class OrderProcessor {
private OrderProcessingStrategy strategy;
public OrderProcessor(OrderProcessingStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(OrderProcessingStrategy strategy) {
this.strategy = strategy;
}
public void process(Order order) {
strategy.process(order);
}
}
使用策略模式
public class Main {
public static void main(String[] args) {
// 创建具体策略
OrderProcessingStrategy normalStrategy = new NormalOrderProcessingStrategy();
OrderProcessingStrategy vipStrategy = new VipOrderProcessingStrategy();
OrderProcessingStrategy groupStrategy = new GroupOrderProcessingStrategy();
// 创建订单处理器并设置初始策略
OrderProcessor processor = new OrderProcessor(normalStrategy);
// 创建订单
Order normalOrder = new Order("normal", 1);
Order vipOrder = new Order("vip", 2);
Order groupOrder = new Order("group", 3);
// 处理订单
processor.process(normalOrder); // 输出:处理普通订单:1
// 动态切换策略
processor.setStrategy(vipStrategy);
processor.process(vipOrder); // 输出:处理会员订单:2
processor.setStrategy(groupStrategy);
processor.process(groupOrder); // 输出:处理团购订单:3
}
}
实际业务场景:订单处理的灵活性
在实际业务中,订单处理的逻辑可能会非常复杂,不同的订单类型可能需要不同的处理流程。策略模式可以帮助我们将这些处理逻辑封装成独立的策略类,从而实现高度的灵活性和可扩展性。
例如,假设未来我们需要增加一个“限时抢购订单”的处理逻辑。我们只需要新增一个具体的策略类,而不需要修改现有的代码:
public class FlashSaleOrderProcessingStrategy implements OrderProcessingStrategy {
@Override
public void process(Order order) {
System.out.println("处理限时抢购订单:" + order.getId());
// 限时抢购订单处理逻辑
}
}
然后在业务逻辑中动态切换策略即可:
OrderProcessingStrategy flashSaleStrategy = new FlashSaleOrderProcessingStrategy();
processor.setStrategy(flashSaleStrategy);
Order flashSaleOrder = new Order("flashSale", 4);
processor.process(flashSaleOrder); // 输出:处理限时抢购订单:4
总结:策略模式的魔法魅力
策略模式是一种非常强大的设计模式,它通过封装算法,让算法的变化独立于使用算法的客户端。无论是前端的表单校验,还是后端的订单处理,策略模式都能让代码变得更加灵活、可扩展,维护起来也更加轻松。
在实际开发中,策略模式的应用场景非常广泛。只要遇到需要根据不同的条件选择不同的算法或行为的场景,都可以考虑使用策略模式。它就像是一个魔法百宝箱,随时可以拿出合适的工具来解决问题。
所以,下次当你再遇到类似的问题时,不妨试试策略模式,让代码变得更加优雅和灵活吧!