设计模式-策略模式

251 阅读4分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

在日常开发中,常遇到这样场景:有多种方式可以完成某个动作,并在未来可能会增加方式,或者对现有的方式进行修改。这个时候可以使用策略模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。

策略模式,是指一种将算法的使用责任和具体实现分割开来的设计模式。它将算法进行包装,并委派给不同的对象管理。

场景

以商场促销场景为例,商场商品的促销方案一般有多种促销方案,例如八折优惠、满200减20等。当我们计算实际支付价格时,倘若不使用设计模式,则需要大量的if...else语句来匹配采用的促销方案。而当我们需要增加促销方案时,需要增加if...else分支,不符合开闭原则,而采用策略模式能很好地解决这些问题。

模式结构

策略模式的结构图

策略模式结构由三类组成:

环境类(Context):环境类指的是使用算法的角色,持有一个算法族对象(即抽象策略类的对象),当需要使用某个算法(策略)时,调用算法族对象的具体算法即可。

抽象策略类(Strategy):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。

具体策略类(Concrete Strategy):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

代码实现

同样以商场促销场景为例,具体实现如下:

环境类接口:

/**
* 商品接口
*/
public abstract class Good {
    // 价格
    BigDecimal price;
    // 商品名
    String name;
    // 促销策略
    PromotionStrategy promotionStrategy;
​
    // 实际价格计算
    abstract BigDecimal calculateActualPrice();
​
}

环境类:

/**
* 商品:水
*/
public class Water extends Good{
    
    public Water(PromotionStrategy promotionStrategy){
        this.price = new BigDecimal("2");
        this.name = "water";
        this.promotionStrategy = promotionStrategy;
    }
    
    @Override
    BigDecimal calculateActualPrice() {
        BigDecimal actualPrice = promotionStrategy.discount(price);
        return actualPrice;
    }
​
}
​
/**
* 商品:面包
*/
public class Bread extends Good{
​
    public Bread(PromotionStrategy promotionStrategy){
        this.price = new BigDecimal("3");
        this.name = "Bread";
        this.promotionStrategy = promotionStrategy;
    }
​
    @Override
    BigDecimal calculateActualPrice() {
        BigDecimal actualPrice = promotionStrategy.discount(price);
        return actualPrice;
    }
​
}

抽象策略类

/*
* 促销策略
*/
public interface PromotionStrategy {
    BigDecimal discount(BigDecimal prince);
}

策略类

/*
* 八折
*/
public class TwentyPercentDiscountPromotionStrategy implements PromotionStrategy{
    @Override
    public BigDecimal discount(BigDecimal prince) {
        return prince.multiply(new BigDecimal("0.8"));
    }
}
​
/*
* 七折
*/
public class ThirtyPercentDiscountPromotionStrategy implements PromotionStrategy{
​
    @Override
    public BigDecimal discount(BigDecimal prince) {
        return prince.multiply(new BigDecimal("0.7"));
    }
}

测试

public class StrategyTest {
    public static void main(String[] args) {
        // 七折策略
        ThirtyPercentDiscountPromotionStrategy thirtyPercentDiscountPromotionStrategy = new ThirtyPercentDiscountPromotionStrategy();
        // 打七折的水
        Water water = new Water(thirtyPercentDiscountPromotionStrategy);
        BigDecimal actualPrice = water.calculateActualPrice();
        System.out.println("水七折促销后的价格是"+actualPrice);
        TwentyPercentDiscountPromotionStrategy twentyPercentDiscountPromotionStrategy = new TwentyPercentDiscountPromotionStrategy();
       // 水改八折优惠
        water.promotionStrategy = twentyPercentDiscountPromotionStrategy;
        System.out.println("水八折促销后的价格是"+water.calculateActualPrice());
        // 八折优惠的面包
        Bread bread = new Bread(twentyPercentDiscountPromotionStrategy);
        System.out.println("面包八折促销后的价格是"+bread.calculateActualPrice());
    }
}

测试结果如下:

image-20210819162203559

优点

  1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。
  2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

缺点

  • 每个策略都需要单独实现一个类,当策略很多时,会产生大量的策略类,会使代码出现“膨胀”。
  • 客户端必须知道所有的策略。
  • 策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能被嵌套使用。