行为篇-策略模式


前言

策略,古时也称“计”,指为了达成某个目标而提前策划好的方案。但计划往往不如变化快,当目标突变或者周遭情况不允许实施某方案的时候,我们就得临时变更方案。策略模式(Strategy)强调的是行为的灵活切换,比如一个类的多个方法有着类似的行为接口,可以将它们抽离出来作为一系列策略类,在运行时灵活对接,变更其算法策略,以适应不同的场景。


提示:以下是本篇文章正文内容,下面案例可供参考

一、“顽固不化”的系统

一个设计优秀的系统,绝不能来回更改底层代码,而是要站在高层抽象的角度构筑一套相对固化的模式,并能使新加入的代码以实现类的方式接入系统,让系统功能得到无限的算法扩展,以适应用户需求的多样性。

我们先从一个反例开始,了解一个有设计缺陷的系统。流行于20世纪80年代的便携式掌上游戏机的系统设计非常简单,最常见的是“俄罗斯方块”游戏机。这种游戏机只能玩一款游戏,所以玩家逐渐减少,最终退出了市场。这是一种嵌入式系统设计,主机不包含任何操作系统。制造商只是简单地将软件固化在游戏机芯片中,造成游戏(软件)与游戏机(硬件)的强耦合。玩家要想换个游戏就得再购买一台游戏机,严重缺乏可扩展性。

1.计算器类

这种耦合性极高的系统设计类似的还有计算器,它只能用于简单的数学运算,算法功能到此为止,没有后续扩展的可能性。我们就以计算器为例,探讨一下这种设计存在的问题。假设计算器可以进行加减法运算。

public class Calculator {

    public int add(int a, int b) {//加法
        return a + b;
    }

    public int sub(int a, int b) {//减法
        return a + b;
    }
}

注意:

  1. 如代码所示,我们分别为计算器类定义了加减法,看上去简单易懂。然而随着算法的不断增加,如乘法、除法、乘方、开方等,我们不得不把机器拆开,然后对代码进行修改。当然,对计算器这种嵌入式系统来说,这么做也无可厚非,毕竟其功能有限且相对固定,但若换作一个庞大的系统,反复的代码修改会让系统维护变成灾难,最终大量的方法被堆积在同一个类中,臃肿不堪。

二、游戏卡带

通过分析和对比代码清中的计算器类,我们不难发现,不管是何种算法(加、减、乘、除等),都属于运算。从外部来看,它们都是基于对两个数字型入参的运算接口,并能返回数字型的运算结果。既然如此,不如把这些算法抽离出来,使它们独立于计算器,并各自封装,让一种算法对应一个类,要使用哪种算法时将其接入即可,如此算法扩展便得到了保证。这种设计上的演变不正类似于从嵌入式掌上游戏机到可插卡式游戏机的演变吗?

1.算法策略接口

策略与系统分离的设计看起来非常灵活,基于这种设计思想,我们对计算器类进行重构。首先要对一系列的算法进行接口抽象,也就是为所有的算法(加法、减法或者即将加入的其他算法)定义一个统一的算法策略接口

public interface Strategy {
    public int calculate(int a,int b);//操作数a,被操作数b
}

要点:

  1. 为保持简单,我们假设算法策略接口的参数与返回结果都是整数,接收参数为操作数a与被操作数b,通过运算后返回结果。

2.具体策略类

//加法策略类
public class Addition implements Strategy{
    @Override
    public int calculate(int a, int b) {
        return a + b;
    }
}
//减法策略类
public class Subtraction implements Strategy{
    @Override
    public int calculate(int a, int b) {
        return a - b;
    }
}

要点:

  1. 算法策略接口Strategy的标准规范化使它们同属一系,但又以类划分,相对独立。

3.重构计算器类

public class Calculator {

    private Strategy strategy;//算法策略接口

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public int getResult(int a, int b){
        return this.strategy.calculate(a, b);//返回具体策略的运算结果
    }
}

要点:

  1. 计算器类里已经不存在具体的加减法运算实现了,取而代之的是对算法策略接口strategy的计算方法calculate()的调用,而具体使用的是哪种算法策略则完全取决于setStrategy()方法。
  2. 注入不同的算法策略将会得到不同的响应结果。
  3. 可以设计为getResult()方法加一个参数为策略接口,用jdk8新特性进行方法引用可以达成同样的效果。

4.客户端类

public class Client {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();//实例化计算器
        calculator.setStrategy(new Addition());//注入加法策略实现
        System.out.println(calculator.getResult(1,1));//输出结果为2

        calculator.setStrategy(new Subtraction());//再注入减法策略实现
        System.out.println(calculator.getResult(1, 1));//输出结果为0
    }
}

说明:

  1. 显而易见,通过重构的计算器类变得非常灵活,不管进行哪种运算,我们只需注入相应的算法策略即可得到结果。此外,今后若要进行功能扩展,只需要新增兼容策略接口的算法策略类(如乘法、除法等),这与插卡式游戏机的策略如出一辙,我们不必再对系统做任何修改便可实现功能的无限扩展。

三、万能的USB接口

相信计算机、USB接口还有各种设备之间的关系以及使用方法都非常熟悉了,这些模块组成的系统正是策略模式的最佳范例

1.USB接口

与之前的计算器实例类似,首先我们定义策略接口。

public interface USB {
    public void read();
}

2.策略实现类

//键盘
public class KeyBoard implements USB{
    @Override
    public void read() {
        System.out.println("键盘指令数据......");
    }
}
//鼠标
public class Mouse implements USB{
    @Override
    public void read() {
        System.out.println("鼠标指令数据......");
    }
}
//摄像头
public class Camera implements USB{
    @Override
    public void read() {
        System.out.println("视频流指令数据......");
    }
}

说明:

  1. 所有USB设备都实现了USB接口的读取数据方法read(),如键盘设备捕获的是键盘指令数据,鼠标设备捕获的是坐标与点击指令数据,摄像头设备捕获的是视频流数据。

3.计算机主机类

public class Computer {

    private USB usb;//主机上的USB接口

    public void setUsb(USB usb) {
        this.usb = usb;
    }

    public void compute(){
        usb.read();
    }
}

4.客户端类

public class Client {
    public static void main(String[] args) {
        Computer com = new Computer();

        com.setUsb(new KeyBoard());//插入键盘
        com.compute();

        com.setUsb(new Mouse());//插入鼠标
        com.compute();

        com.setUsb(new Camera());//插入摄像头
        com.compute();
    }
}
输出结果:
键盘指令数据......
鼠标指令数据......
视频流指令数据......

说明:

  1. 我们通过对计算机USB接口的标准化,使计算机系统拥有了无限扩展外设的能力,需要什么功能只需要购买相关的USB设备。可见在策略模式中,USB接口起到了至关重要的解耦作用。如果没有USB接口的存在,我们就不得不将外设直接“焊接”在主机上,致使设备与主机高度耦合,系统将彻底丧失对外设的替换与扩展能力。

总结

提示:这里对文章进行总结:

  1. 策略模式让策略与系统环境彻底解耦,通过对算法策略的抽象、拆分,再拼装、接入外设,使系统行为的可塑性得到了增强。策略接口的引入也让各种策略实现彻底解放,最终实现算法分立,即插即用。

  2. 策略模式的各角色定义如下:

    • Strategy(策略接口):定义通用的策略规范标准,包含在系统环境中并声明策略接口标准。对应本章例程中的USB接口USB。
    • ConcreteStrategyA、ConcreteStrategyB、ConcreteStrategyC……(策略实现):实现了策略接口的策略实现类,可以有多种不同的策略实现,但都得符合策略接口定义的规范。对应本章例程中的USB键盘类Keyboard、USB鼠标类Mouse、USB摄像头类Camera。
    • Context(系统环境):包含策略接口的系统环境,对外提供更换策略实现的方法setStrategy()以及执行策略的方法executeStrategy(),其本身并不关心执行的是哪种策略实现。对应本章例程中的计算机主机类Computer。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhixuChen200

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值