设计模式之策略模式(行为型)

Posted 一起来搬砖呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之策略模式(行为型)相关的知识,希望对你有一定的参考价值。

1、策略模式定义

策略模式的定义:策略模式是一种行为型模式,定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

比如商场收银时如何促销,用打折还是返利,其实都是一些算法,这些算法本身就是一种策略,这些算法是随时都可以互相替换的,这就是变化点,而封装变化是我们面向对象的一种很重要的思维方式

2、策略模式结构

Strategy 类,定义所有支持的算法的公共接口

/**
 * 抽象策略类
 */
public abstract class Strategy 
    
    public abstract void  algorithmInterface();

ConcreteStrategy,封装了具体的算法或行为,继承于 Strategy

/**
 * 具体算法 A
 */
public class ConcreteStrategyA extends Strategy

    /**
     * 算法 A 实现
     */
    @Override
    public void algorithmInterface() 
        System.out.println("算法 A 实现");
    


/**
 * 具体算法 B
 */
public class ConcreteStrategyB extends Strategy 
    
    /**
     * 算法 B 实现
     */
    @Override
    public void algorithmInterface() 
        System.out.println("算法 B 实现");
    


/**
 * 具体算法 C
 */
public class ConcreteStrategyC extends Strategy 

    /**
     * 算法 C 实现
     */
    @Override
    public void algorithmInterface() 
        System.out.println("算法 C 实现");
    

Context,用一个 ConcreteStrategy 来配置,维护一个对 Strategy 对象的引用

/**
 * 上下文
 */
public class Context 

    private Strategy strategy;

    public Context() 
    

    /**
     * 初始化时传入具体的策略对象
     * @param strategy 具体的策略对象
     */
    public Context(Strategy strategy) 
        this.strategy = strategy;
    

    /**
     * 上下文接口
     */
    public void contextInterface() 
        strategy.algorithmInterface();
    


调用方代码

public class StrategyTest 

    /**
     * 因为实例化不同的策略,所以在调用 context.contextInterface(); 时,得到的结果不同
     */
    public static void main(String[] args) 
        Context context;

        context = new Context(new ConcreteStrategyA());
        context.contextInterface();

        context = new Context(new ConcreteStrategyB());
        context.contextInterface();

        context = new Context(new ConcreteStrategyC());
        context.contextInterface();
    


3、策略模式优缺点

优点:

  • 由于策略类实现的是同一个抽象,策略类之间可以自由切换
  • 对扩展开放,可以不改动原有代码,增加一个新的策略对策略模式来说比较容易

缺点:

  • 维护策略类会有额外开销,策略类数量多了的话会比较麻烦
  • 必须对调用方暴露所有的策略类,因为使用哪种策略由调用方决定,调用方需要了解每种策略及它们之间的区别

4、策略模式的应用场景

  • 几个类的主要逻辑相同,只是部分逻辑的算法和行为上有一些区别的情况
  • 有几种相似的行为,客户端动态的决定使用哪种,我们可以使用策略模式将算法封装起来供客户端使用

5、策略模式案例实现

商场收银

比如商场使用现金收银时,有正常收费 CashNormal、打折收费 CashRebate、和返利收费 CashReturn 三个策略,也就是策略模式中的具体算法,我们使用策略模式来做这样一个设计

代码结构图

现金收费抽象类

/**
 * 抽象现金策略,收取现金,返回价格
 */
public abstract class CashStrategy 

    /**
     * 接收现金
     * @param money          接收的现金金额
     * @param moneyRebate    打折率
     * @param moneyCondition 返利条件
     * @param moneyReturn    返利值
     * @return 最后的金额
     */
    public abstract BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn);

    /**
     * 策略类型
     * @return 策略类型
     */
    public abstract String type();


正常收费策略

/**
 * 正常收费子类,原价返回
 */
@Component
public class CashNormalStrategy extends CashStrategy 

    @Override
    public BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn) 
        return money;
    

    @Override
    public String type() 
        return "normal";
    


打折收费策略

/**
 * 打折收费算法,初始化传入折扣,返回打折后金额
 */
@Component
public class CashRebateStrategy extends CashStrategy 

    @Override
    public BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn) 
        return money.multiply(BigDecimal.valueOf(moneyRebate > 0 ? moneyRebate : 1.0d)).setScale(2);
    

    @Override
    public String type() 
        return "rebate";
    


返利收费策略

/**
 * 返利收费算法,初始化传入返利条件和返利值
 */
@Component
public class CashReturnStrategy extends CashStrategy 

    @Override
    public BigDecimal acceptCash(BigDecimal money, Double moneyRebate, Double moneyCondition, Double moneyReturn) 
        BigDecimal result = money;
        BigDecimal condition = BigDecimal.valueOf(moneyCondition == null ? 300.0d : moneyCondition);
        BigDecimal returnMoney = BigDecimal.valueOf(moneyReturn == null ? 100.0d : moneyReturn);
        // 如果大于返利条件,要减去返利值
        if (money.compareTo(condition) > -1) 
            result = money.subtract(money.divideAndRemainder(condition)[0].multiply(returnMoney));
        
        return result.setScale(2);
    

    @Override
    public String type() 
        return "return";
    


现金收费决策,根据类型选择策略

/**
 * 现金策略决策,根据策略类型获取不同策略
 */
@Component
public class CashStrategyDecider 

    @Resource
    private CashNormalStrategy normalStrategy;
    @Resource
    private CashRebateStrategy rebateStrategy;
    @Resource
    private CashReturnStrategy returnStrategy;

    public CashStrategy decideCashStrategy(String strategyType) 
        if (strategyType == null ) 
            return normalStrategy;
        
        if (strategyType.equals(rebateStrategy.type())) 
            return rebateStrategy;
         else 
            // todo 扩展点
            if (strategyType.equals(returnStrategy.type())) 
                return returnStrategy;
            
        
        return normalStrategy;
    

调用代码:

public class CashStrategyTest extends BaseTest 

    @Resource
    private CashStrategyDecider cashStrategyDecider;

    @Test
    public void cashStrategyTest() 
        CashStrategy cashStrategy;

        // 默认算法
        cashStrategy = cashStrategyDecider.decideCashStrategy("");
        BigDecimal defaultResult = cashStrategy.acceptCash(new BigDecimal(200), null, null, null);
        System.out.println("使用默认算法得到的金额为" + defaultResult.doubleValue()); // 使用默认算法得到的金额为200.0

        // 打折算法
        cashStrategy = cashStrategyDecider.decideCashStrategy("rebate");
        BigDecimal rebateResult = cashStrategy.acceptCash(new BigDecimal(200), 0.8d, null, null);
        System.out.println("使用打折算法得到的金额为" + rebateResult.doubleValue()); // 使用打折算法得到的金额为160.0

        // 返利算法
        cashStrategy = cashStrategyDecider.decideCashStrategy("return");
        BigDecimal returnResult = cashStrategy.acceptCash(new BigDecimal(500), null, 300d, 100.0d);
        System.out.println("使用返利算法得到的金额为" + returnResult.doubleValue()); // 使用返利算法得到的金额为400.0
    


6、策略模式解析

  • 策略模式是一种定义一系列算法的方法,从概念上来看,这些所有的算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合
  • 对于打折、返利或者其它算法,其实都是对实际商品收费的一种计算方式,通过继承就可以得到它们的公共功能,这就让它们有了抽象的父类 CashStrategy
  • 策略模式简化了单元测试,每个算法都有自己的类,可以通过自己的接口单独测试,每个算法可以保证它没有错误,修改其中一个也不会影响其它算法
  • 策略模式封装了变化,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性
  • 基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的 Context 对象,相对来说比较好的办法是选择具体实现的职责也由 Context 来承担

以上是关于设计模式之策略模式(行为型)的主要内容,如果未能解决你的问题,请参考以下文章

设计模式(二十二)—— 策略模式

23种设计模式---策略设计模式

设计模式——行为型模式之策略模式

设计模式之策略模式(行为型)

行为型模式之策略模式

行为型模式之策略模式