前端小白变形记:你要学会这些设计模式!第六弹:策略模式

443 阅读7分钟

前言

  1. 从第一篇工厂模式开始,我会持续地更新每一种设计模式的内容,争取用通俗易懂的语言讲解和解释清楚。如果对你学习设计模式有帮助,请不要吝啬手中的赞~ 如果对文章内容有任何疑惑都可以在评论区提出和讨论~

  2. 本系列文章中的完整源码已上传 github 仓库,你可以在这里 github.com/FatMii/Desi…获取。

    同样的,如果对你有帮助,请给我一个star~谢谢

  3. 设计模式合集链接:

    前端小白变形记:你要学会这些设计模式!首发:工厂模式

    前端小白变形记:你要学会这些设计模式!第二弹:单例模式

    前端小白变形记:你要学会这些设计模式!第三弹:责任链模式

    前端小白变形记:你要学会这些设计模式!第四弹:观察者模式

    前端小白变形记:你要学会这些设计模式!第五弹:发布订阅模式

Hello~大家好,本篇文章我们继续学习设计模式第六篇:策略模式模式

一、介绍

策略模式是一种行为型设计模式,其核心在于将一系列算法封装起来,使它们可以互换使用。这样做的好处是,你可以根据不同的需求灵活选择算法,而无需每次都重新编写代码。

策略模式主要包含以下三个关键部分:

  1. 策略接口(Strategy) :这是一个通用的接口,定义了所有算法的公共操作。所有具体的算法都必须实现这个接口,从而保证它们可以被统一调用。
  2. 具体策略(ConcreteStrategy) :这些是实现了策略接口的具体算法。每个具体策略都提供了一种特定的实现方式,用于完成特定的任务。
  3. 上下文(Context) :这是一个封装类,它持有一个策略接口的引用,并通过这个引用来调用具体的算法。上下文类负责根据不同的需求选择合适的策略,并委托其执行任务。它并不关心具体策略的实现细节,只关注策略接口的定义。

通过这种设计,策略模式能够将算法的定义与使用分离开来,使得算法可以独立于客户端变化,从而提高了系统的灵活性和可扩展性。

二、前端场景:表单规则校验

在前端开发中,表单校验是一个非常常见的功能。我们经常需要对用户的输入进行各种校验,比如用户名不能为空、密码长度必须大于6位、邮箱格式是否正确等等。如果用传统的 if-elseswitch-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-elseswitch-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

总结:策略模式的魔法魅力

策略模式是一种非常强大的设计模式,它通过封装算法,让算法的变化独立于使用算法的客户端。无论是前端的表单校验,还是后端的订单处理,策略模式都能让代码变得更加灵活、可扩展,维护起来也更加轻松。

在实际开发中,策略模式的应用场景非常广泛。只要遇到需要根据不同的条件选择不同的算法或行为的场景,都可以考虑使用策略模式。它就像是一个魔法百宝箱,随时可以拿出合适的工具来解决问题。

所以,下次当你再遇到类似的问题时,不妨试试策略模式,让代码变得更加优雅和灵活吧!