Bootstrap

设计模式之策略模式

一、引言

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

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

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

(一)角色分析

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

三、Java 代码实现策略模式

(一)创建抽象策略角色

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

public interface DiscountStrategy {

double calculateDiscount(double price);

}

(二)创建具体策略角色

无折扣策略

public class NoDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double price) {

return price;

}

}

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

public class FixedDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double price) {

return price * 0.8;

}

}

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

public class FullReductionDiscountStrategy implements DiscountStrategy {

@Override

public double calculateDiscount(double price) {

if (price >= 100) {

return price - 20;

}

return price;

}

}

(三)创建环境角色

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);

}

}

(四)测试代码

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 语句来选择不同的算法,这样的代码不仅难以阅读和维护,而且当新增算法时,需要修改这个复杂的条件判断语句。而策略模式通过将不同算法封装成独立的类,避免了这种多重条件判断的情况。

五、策略模式的应用场景

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

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

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

六、策略模式的优缺点

(一)优点

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

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

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

(二)缺点

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

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

;