设计模式之策略模式

一、引言

在软件开发过程中,我们常常会遇到这样的情况:针对同一个问题,有多种不同的解决方案。比如在排序算法中,有冒泡排序、快速排序、归并排序等。如果我们把这些不同的算法都写在一个类中,通过条件判断来选择使用哪种算法,这样的代码不仅冗长,而且可维护性和扩展性都很差。策略模式正是为了解决这类问题而诞生的。

二、策略模式的定义与概念

策略模式定义了一系列算法,将每个算法都封装起来,并且使它们之间可以互换。策略模式使算法的变化独立于使用算法的客户。

(一)角色分析

  1. 抽象策略角色(Strategy):定义了一个公共接口,各种具体的策略类实现这个接口。该接口规定了具体策略类必须实现的方法。
  1. 具体策略角色(ConcreteStrategy):实现了抽象策略角色所定义的接口,提供具体的算法实现。
  1. 环境角色(Context):持有一个抽象策略类的引用,提供一个方法来设置具体策略类,以便在运行时根据需要切换策略。

三、Java 代码实现策略模式

(一)创建抽象策略角色

首先,我们定义一个抽象的策略接口,以计算折扣为例。

java 复制代码
public interface DiscountStrategy {

double calculateDiscount(double price);

}

(二)创建具体策略角色

无折扣策略

java 复制代码
public class NoDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double price) {

return price;

}

}

固定折扣策略(例如 8 折)

java 复制代码
public class FixedDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double price) {

return price * 0.8;

}

}

满减折扣策略(例如满 100 减 20)

java 复制代码
public class FullReductionDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double price) {

if (price >= 100) {

return price - 20;

}

return price;

}

}

(三)创建环境角色

java 复制代码
public class ShoppingCart {

private DiscountStrategy discountStrategy;

public ShoppingCart(DiscountStrategy discountStrategy) {

this.discountStrategy = discountStrategy;

}

public void setDiscountStrategy(DiscountStrategy discountStrategy) {

this.discountStrategy = discountStrategy;

}

public double calculateTotalPrice(double originalPrice) {

return discountStrategy.calculateDiscount(originalPrice);

}

}

(四)测试代码

java 复制代码
public class StrategyPatternTest {

public static void main(String[] args) {

// 使用无折扣策略

ShoppingCart cart1 = new ShoppingCart(new NoDiscountStrategy());

double price1 = 150;

double total1 = cart1.calculateTotalPrice(price1);

System.out.println("无折扣时,总价为:" + total1);

// 使用固定折扣策略

ShoppingCart cart2 = new ShoppingCart(new FixedDiscountStrategy());

double price2 = 150;

double total2 = cart2.calculateTotalPrice(price2);

System.out.println("8 折折扣时,总价为:" + total2);

// 使用满减折扣策略

ShoppingCart cart3 = new ShoppingCart(new FullReductionDiscountStrategy());

double price3 = 150;

double total3 = cart3.calculateTotalPrice(price3);

System.out.println("满 100 减 20 折扣时,总价为:" + total3);

// 动态切换策略

cart3.setDiscountStrategy(new FixedDiscountStrategy());

double total4 = cart3.calculateTotalPrice(price3);

System.out.println("切换为 8 折折扣后,总价为:" + total4);

}

}

四、策略模式的关键要点

(一)封装变化

将不同的算法封装在各自的具体策略类中,这样当需要新增或修改算法时,只需要修改或新增具体策略类,而不会影响到其他部分的代码。例如,如果我们要新增一种 "买一送一" 的折扣策略,只需要创建一个新的具体策略类实现 DiscountStrategy 接口,而不需要修改 ShoppingCart 类或其他已有的策略类。

(二)提高可维护性和扩展性

通过将算法独立封装,使得代码结构更加清晰,易于维护。同时,新增策略也非常方便,符合开闭原则。例如,在电商系统中,如果需要增加新的促销活动策略,直接添加新的策略类即可,而不需要在一个庞大的类中添加大量的条件判断代码。

(三)运行时动态切换

环境角色可以在运行时根据不同的条件动态切换具体的策略。比如在上述购物车的例子中,我们可以根据用户的会员等级、活动时间等条件,在运行时动态地为购物车设置不同的折扣策略,实现灵活的业务逻辑。

(四)避免多重条件判断

如果不使用策略模式,我们可能会在一个方法中使用大量的 if - else 或 switch - case 语句来选择不同的算法,这样的代码不仅难以阅读和维护,而且当新增算法时,需要修改这个复杂的条件判断语句。而策略模式通过将不同算法封装成独立的类,避免了这种多重条件判断的情况。

五、策略模式的应用场景

算法的选择:在排序算法、搜索算法等场景中,不同的算法适用于不同的数据规模和数据特点。使用策略模式可以根据具体情况选择合适的算法。

业务规则的变化:在电商系统中,促销活动规则经常变化,如不同节日的不同折扣策略。策略模式可以很好地应对这种变化,方便地添加或修改促销规则。

行为的动态变化:游戏开发中,角色的移动方式、攻击方式等行为可以使用策略模式实现动态变化。例如,角色在不同场景下可能有不同的移动速度和攻击方式。

六、策略模式的优缺点

(一)优点

灵活性高:可以根据不同的需求动态切换算法,提高了系统的灵活性。

可维护性好:每个策略类都是独立的,修改一个策略类不会影响其他策略类和系统的其他部分。

符合开闭原则:易于扩展新的策略,满足不断变化的业务需求。

(二)缺点

客户端需要了解不同的策略:客户端在使用策略模式时,需要知道有哪些具体的策略可供选择,并且要根据实际情况选择合适的策略。这可能会增加客户端的使用难度。

策略类数量增多:如果有大量的策略,会导致策略类的数量增多,增加系统的复杂性。

相关推荐
用户938169125536020 分钟前
Head First 模板方法模式
设计模式
是糖糖啊4 小时前
OpenSpec 完整使用流程笔记 (SDD)
设计模式
程序员Terry1 天前
同事被深拷贝坑了3小时,我教他原型模式的正确打开方式
java·设计模式
刀法如飞2 天前
AI时代,程序员都应该是算法思想工程师
人工智能·设计模式·程序员
在西安放羊的牛油果2 天前
我把 2000 行下单代码,重构成了一套交易前端架构
前端·设计模式·架构
寅时码3 天前
React 正在演变为一场不可逆的赛博瘟疫:AI 投毒、编译器迷信与装死的官方
前端·react.js·设计模式
willow6 天前
Axios由浅入深
设计模式·axios
七月丶8 天前
别再手动凑 PR 了:这个 AI Skill 会按仓库习惯自动建分支、拆提交、提 PR
人工智能·设计模式·程序员
刀法如飞8 天前
从程序员到架构师:6大编程范式全解析与实践对比
设计模式·系统架构·编程范式