策略模式(Strategy Pattern)

在讲策略模式之前,我们先举个例子
假设现在要实现一个计算器程序,如果让你来设计,你会不会这么写?校验不是重点所以忽略

package cn.limingcheng.strategy;

import java.math.BigDecimal;

public class Calculator {
    /**
     * 两个数加减乘除操作
     *
     * @param val1     第一个数字
     * @param val2     第二个数字
     * @param calcType 计算方式
     * @return
     */
    public BigDecimal calc(BigDecimal val1, BigDecimal val2, String calcType) {
        if ("+".equals(calcType)) {
            return val1.add(val2);
        } else if ("-".equals(calcType)) {
            return val1.subtract(val2);
        } else if ("*".equals(calcType)) {
            return val1.multiply(val2);
        } else if ("/".equals(calcType)) {
            return val1.divide(val2);
        }
        return null;
    }
}

代码可以很稳定的运行,但是如果现在增加求两个数余数怎么办?再增加个if else?很明显不符合开闭原则,修改原来的代码可能会造成想不到的结果,所以先做个小改进,将算法剥离出来

package cn.limingcheng.strategy;

import java.math.BigDecimal;

public class Calculator {
    /**
     * 两个数加减乘除操作
     *
     * @param val1     第一个数字
     * @param val2     第二个数字
     * @param calcType 计算方式
     * @return
     */
    public BigDecimal calc(BigDecimal val1, BigDecimal val2, String calcType) {
        if ("+".equals(calcType)) {
            return add(val1, val2);
        } else if ("-".equals(calcType)) {
            return subtract(val1, val2);
        } else if ("*".equals(calcType)) {
            return multiply(val1, val2);
        } else if ("/".equals(calcType)) {
            return divide(val1, val2);
        }
        return null;
    }

    /**
     * 加法
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    private BigDecimal add(BigDecimal val1, BigDecimal val2) {
        return val1.add(val2);
    }

    /**
     * 减法
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    private BigDecimal subtract(BigDecimal val1, BigDecimal val2) {
        return val1.subtract(val2);
    }

    /**
     * 乘法
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    private BigDecimal multiply(BigDecimal val1, BigDecimal val2) {
        return val1.multiply(val2);
    }

    /**
     * 除法
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    private BigDecimal divide(BigDecimal val1, BigDecimal val2) {
        return val1.divide(val2);
    }
}

上面代码相比第一种将算法剥离出来,当只有一个算法改动时只需要修改相应的即可,不过问题依旧存在,当增加求余数方法的时候还要修改原来的类,这时策略模式闪亮登场

策略模式:定义了算法族分别封装起来,它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。八字总结:分离算法,选择实现

UML类图如下:

策略模式类图

UML时序图如下:

策略模式时序图

结构:

  • 策略(Strategy): 定义所有支持的算法的公共接口,Context可持该对象的引用,符合面向接口编程
  • 策略实现(ConcreteStrategy):以 Strategy 接口实现某具体算法
  • 上下文(Context):负责和具体的策略实现交互,内部会持有一个Strategy接口的引用,给客户端调用

编写步骤:

  1. 定义抽象策略角色(为策略对象定义一个公共的接口)
  2. 编写具体策略角色(实际上就是实现上面定义的公共接口)
  3. 定义上下文角色,内部持有策略接口的引用

代码体现

第一步定义策略接口角色

package cn.limingcheng.strategy;

/**
 * 策略接口
 */
public interface IStrategy {
    /**
     * 定义抽象方法约束具体算法的实现
     */
    public void algorithmInterface();
}

第二步编写具体策略角色,我这边定义两个:ConcreteStrategyA 和 ConcreteStrategyB

package cn.limingcheng.strategy;

/**
 * 策略的具体实现A
 */
public class ConcreteStrategyA implements IStrategy {
    /**
     * 具体的算法实实现
     */
    @Override
    public void algorithmInterface() {
        System.out.println("ConcreteStrategyA method");
    }
}
package cn.limingcheng.strategy;

/**
 * 策略的具体实现B
 */
public class ConcreteStrategyB implements IStrategy {
    /**
     * 具体的算法实实现
     */
    @Override
    public void algorithmInterface() {
        System.out.println("ConcreteStrategyB method");
    }
}

第三步定义上下文角色

package cn.limingcheng.strategy;

/**
 * 策略上下文
 */
public class Context {

    /**
     * 面向接口编程,持有策略接口
     */
    private IStrategy strategy;

    /**
     * 通过构造器注入一个具体的策略实现类
     *
     * @param strategy 具体的策略
     */
    public Context(IStrategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 调用策略实现的方法
     */
    public void call() {
        strategy.algorithmInterface();
    }
}

最后看成果吧,创建一个客户端程序测试

package cn.limingcheng.strategy;

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 实例化具体要使用的策略对象
        IStrategy strategy = new ConcreteStrategyA();
        // 创建策略上下文并且传入具体策略对象
        Context context = new Context(strategy);
        // 调用上下文提供的方法来完成对具体策略实现的回调
        context.call();
    }
}

接下来让我们用策略模式改造计算器程序吧

先创建计算器策略接口

package cn.limingcheng.strategy.calc;

import java.math.BigDecimal;

/**
 * 计算器策略接口
 */
public interface ICalculatorStrategy {
    /**
     * 两个数加减乘除操作
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    public BigDecimal calc(BigDecimal val1, BigDecimal val2);
}

实现具体策略,为了篇幅这边就写加法以及减法的实现类

package cn.limingcheng.strategy.calc;

import java.math.BigDecimal;

/**
 * 加法策略实现类
 */
public class AddCalculatorStrategy implements ICalculatorStrategy {
    /**
     * 实现加法算法
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    @Override
    public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
        return val1.add(val2);
    }
}
package cn.limingcheng.strategy.calc;

import java.math.BigDecimal;

/**
 * 减法策略实现类
 */
public class SubtractCalculatorStrategy implements  ICalculatorStrategy {
    /**
     * 实现减法算法
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    @Override
    public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
        return val1.subtract(val2);
    }
}

定义计算器策略上下文

package cn.limingcheng.strategy.calc;

import java.math.BigDecimal;

/**
 * 计算器策略上下文
 */
public class CalculatorContext {
    /**
     * 持有计算器策略接口引用
     */
    private ICalculatorStrategy calculatorStrategy;

    /**
     * 通过构造器注入具体计算器策略对象
     *
     * @param calculatorStrategy
     */
    public CalculatorContext(ICalculatorStrategy calculatorStrategy) {
        this.calculatorStrategy = calculatorStrategy;
    }

    /**
     * 对外提供方法实现对具体策略的方法调用
     *
     * @param val1
     * @param val2
     * @return
     */
    public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
        return calculatorStrategy.calc(val1, val2);
    }
}

创建客户端进行测试

package cn.limingcheng.strategy.calc;

import java.math.BigDecimal;

/**
 * 计算器客户端
 */
public class CalculatorClient {
    public static void main(String[] args) {
        // 实例化加法策略
        ICalculatorStrategy add = new AddCalculatorStrategy();
        // 实例化计算器策略上下文
        CalculatorContext calculatorContext = new CalculatorContext(add);
        // 调用具体策略算法
        BigDecimal result = calculatorContext.calc(new BigDecimal("10"), new BigDecimal(20));
        System.out.println(result);
    }
}

使用策略模式改造计算器代码后,试想如果想增加求余数的策略怎么办?我们只需要增加一个求余数的策略实现类

package cn.limingcheng.strategy.calc;

import java.math.BigDecimal;

/**
 * 余数策略实现类
 */
public class RemainderCalculatorStrategy implements ICalculatorStrategy {
    /**
     * 实现余数算法
     *
     * @param val1 第一个数字
     * @param val2 第二个数字
     * @return
     */
    @Override
    public BigDecimal calc(BigDecimal val1, BigDecimal val2) {
        return val1.remainder(val2);
    }
}

客户端指定策略为余数策略即可

package cn.limingcheng.strategy.calc;

import java.math.BigDecimal;

/**
 * 余数客户端
 */
public class RemainderCalculatorClient {
    public static void main(String[] args) {
        // 实例化余数策略
        ICalculatorStrategy remainder = new RemainderCalculatorStrategy();
        // 实例化计算器策略上下文
        CalculatorContext remainderCalculatorContext = new CalculatorContext(remainder);
        // 调用具体策略算法
        BigDecimal remaindeResult = remainderCalculatorContext.calc(new BigDecimal("10"), new BigDecimal(3));
        System.out.println(remaindeResult);
    }
}

这样改造符合三个设计原则

  • 开闭原则,增加功能只是扩展,并没有修改原代码
  • 依赖导致原则,采用面向接口编程,策略上下文引用策略接口,而不是针对具体实现编程
  • 里氏替换原则,基类之间可以相互替换

策略模式设计规范:

  • 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
  • 面向接口编程,不是针对具体实现编程
  • 多用组合、少用继承

策略模式优点

  1. 策略模式的功能就是通过抽象、封装来定义一系列的算法,使得这些算法可以相互替换,所以为这些算法定义一个公共的接口,以约束这些算法的功能实现。如果这些算法具有公共的功能,可以将接口变为抽象类,将公共功能放到抽象父类里面。
  2. 策略模式的一系列算法是可以相互替换的、是平等的,写在一起就是if-else组织结构,如果算法实现里又有条件语句,就构成了多重条件语句,可以用策略模式,避免这样的多重条件语句
  3. 扩展性更好:在策略模式中扩展策略实现非常的容易,只要新增一个策略实现类,然后在使用策略实现的地方,使用这个新的策略实现就好了

策略模式缺点

  1. 客户端必须了解所有的策略,清楚它们的不同
  2. 增加了对象的数量,由于策略模式将每个具体的算法都单独封装为一个策略类,如果可选的策略有很多的话,那对象的数量也会很多。
  3. 只适合偏平的算法结构,由于策略模式的各个策略实现是平等的关系(可相互替换),实际上就构成了一个扁平的算法结构。即一个策略接口下面有多个平等的策略实现(多个策略实现是兄弟关系),并且运行时只能有一个算法被使用。这就限制了算法的使用层级,且不能被嵌套
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值