设计模式-策略模式

最开始了解到策略模式是在微信公众号上,一个叫王二的公共号,推了一条使用策略模式来优化if()…else语句的文章,当时觉得很有用,所以就想学一学这个策略模式,也就造就了今天这篇总结吧。
首先,为什么我会被策略模式优化if()…else语句所吸引到呢,下面看一下这个代码图就知道了。。。
在这里插入图片描述
在这里插入图片描述

上面两张图是我实际代码中的写法,这里简单的说下这个项目的基本流程:项目是做统一支付平台的业务,有支付宝、微信、银联、pos、工行聚合支付等支付渠道,而支付宝中又存在支付宝商户扫码,支付宝用户扫码、支付宝窗口号,微信支付中又存在微信商户扫码、微信用户扫码、微信公众号支付等方式,这也就早就了以上的代码。所以,当我看到使用策略模式替代if()…esle时是有多激动啊!!!
好了,下面进入正题。

1.引出策略模式

在谈策略模式之前,想跟大家分享下网上对23种设计模式的总结,总的来说,可以分为三类:
(1)创建型模式
关注点:关注于对象的创建,同时隐藏创建逻辑;
(2)结构型模式
关注点:关注于类和对象之间的组合;
(3)行为型模式
关注点:关注对象之间的通信;
其中,之前有两篇关于设计模式的文章,单例模式和工厂模式都属于创建型设计模式,而今天的主角是属于行为模式的策略模式,如同上文提到的,它的关注点是对象之间的通信,这里引用下网上的资源,看看行为型模式的各个设计模式的分类:
在这里插入图片描述
从图中可以看出,策略模式的关注点是父类与子类的模式,为什么是父类与子类的关系呢?
下面让我们来从策略模式的官方定义来找找原因:

1.1策略模式官方定义分析

(1)策略模式也叫政策模式,允许程序再运行时选择一个算法执行,通常存在一类算法实现提供外部选择执行,这里的算法,也可以叫做策略。
(2)定义一类算法,各自独立封装实现,并且相互之间时可替换的,除此之外,由客户端类决定具体使用何种算法。

可能在看到这些定义的时候,一定会有一种头大的感觉,下面来看看以下分析:

首先,两个定义种都设计到“算法”这个词,这里提到的算法并不是指“冒泡排序算法”、“搜索算法”之类的算法,它可以是一段代码,一个请求,一个业务操作等。

其次,为什么说是父类与子类的关系呢?来看这句话“通常存在一类算法实现提供外部选择执行,各自独立封装实现,并且相互之间时可替换的”,这句话读起来是不是很熟悉,是不是很像在父类中定义方法,子类继承父类并重写父类中的方法。而实际上,策略模式就是这样做的,不过,很多时候,它是以接口的形式实现,将各个“子类”具有的公共方法抽象封装成接口中的模板方法,你这个“子类”只要去实现接口,就可以拥有接口的方法。可能读到这里你还感觉对策略模式理解起来还是很抽象,可以通过策略模式的图解来辅助理解。
在这里插入图片描述
分析:上图中,有optionA和optionB两个类,也可以叫做上文中提到的“算法”“算法”内有A和B各自的代码业务逻辑,比如do()方法,这里将两个类封装成一个Strategy策略类(接口或者叫父类),在策略类中,封装了处理A和B的do()方法。
然后最重要的一点是这个策略类如何被客户端调用?这里引入了一个Context类,这个类的作用是用于连接Strategy策略类和客户端Client类,叫上下文类,语文阅读理解中经典的“承上启下”的作用。在Context中需传入Strategy策略类的某一个实现类的实例,即A或B的实例对象,然后,在类的内部定义了一个public修饰的executestrategy()方法,客户端可以调用这个方法,获取对应接口实现类A或B的业务逻辑实现。
综上所述,使用策略模式有以下几个步骤。

1.2策略模式的实现步骤

step01--定义抽象策略角色(Stratrgy),即上图中的接口,接口中需包括if()…else语句中所有分支所共有的方法;
step02--定义具体策略角色(ConcreteStrategy),即上图中的A和B接口实现类;
step03--定义环境角色(Context),即上图中的Context,用于连接上下文,将接口实现类A和B推销给客户,可以理解为销售员的角色。
下面以生活中例子来解释下策略模式的基本使用。

2.策略模式的使用

以手机上的计算器为例,计算机具有加、减、乘、除基础功能,起初在手机应用端,用户按下哪个键你并不知道,有可能按+,有可能-,有可能x,有可能/,按照之前的写法,if语句嵌套,肯定是可以实现的,这里就不做介绍,下面来看使用策略模式如何实现。
首先,看一看下面这张图。
在这里插入图片描述
这张图以计算器的加法和减法为例,与策略模式的结构图基本类似,只不过,在策略模式的原型图中,接口是以Stratrgy命名的,上下文类使用Context命名,而这张图使用Operation和Calculator来命名,其实,策略模式并不是一定要命名为Stratrgy和Context的,根据实际业务名称,更突出了策略模式是一种设计模式思想。
下面看具体的代码实现。

2.1策略模式代码实现

完全按照之前介绍的三个步骤来进行演示。

2.1.1定义抽象策略角色(Stratrgy)

在这里插入图片描述

2.1.2定义具体策略角色(ConcreteStrategy)

定义加法实现类
在这里插入图片描述
定义减法实现类
在这里插入图片描述

2.1.3定义环境角色(Context)

在这里插入图片描述
结果显而易见就不做过多的解释了。

3.策略模式的优缺点

看到上几张图,可能你会有疑惑,为什么要把加减乘除四则运算封装到独立的类中?直接放在calculator中以add()、sub()等方法不是更便捷吗?甚至,试想,计算器中不知有加减乘除四个运算,如果加上平方,开方运算,那岂不是要再新建两个实现类吗?这样不就造成代码冗余吗?

面对这一连串的灵魂拷问,没错,你说的都对!

但是,万事都有但是,你说的这些场景都是在你自己写读写自己代码的时候考虑到的问题,假如说把这个计算器的应用分享给同事使用,并且是以jar包的形式引入,这个时候,如果你的同事想给计算机添加平方和开方的功能,这要怎么办,你提供给他的是jar包呀,,,,他要怎么修改你的代码????难道要在calculator类中添加method吗?这是不可能的。
即使你给同事提供的是你的源码,但是,你确定你想要你的同事修改你的源码吗?确定不会造成意料之外的报错吗?这些都是不可预知的!
所以,在这个时候,就体现出了设计模式的重要性了,特别是策略模式的重要性了,只要同事新建一个开方的实现类,就可以完成功能业务开发。

另外,还有一个注意点,在2.1.3图中,在使用calcul.setOperation (new operationAdd ());选择实现类的时候,使用的是new对象的形式,虽然可以满足业务需求,但是加入出现jar包引入的情况,这种new对象的形式就不能用了,这里可以考虑使用反射机制来动态创建对象,可以参考这篇文章java反射机制,可以选择在配置文件中配置类的全路径,使用反射的Class.forName("类的全路径")方式创建对象,进而选择实现类。

4.文章中的代码

4.1operation接口
package com.wshy.testdemo.StrategyDemo;

/**
 * @author wshy
 * @data 2020/7/11
 **/
public interface operation {

    //定义加法和减法的共有方法
    int doOperation(int num1,int num2);

}
4.2operationAdd实现类
package com.wshy.testdemo.StrategyDemo;

/**
 * 加法类,实现operation接口
 * @author wshy
 * @data 2020/7/11
 **/
public class operationAdd implements operation {
    @Override
    public int doOperation (int num1, int num2) {

        return num1 + num2;
    }
}

4.3operationSub实现类
package com.wshy.testdemo.StrategyDemo;

/**
 * 减法类,实现operation接口
 * @author wshy
 * @data 2020/7/11
 **/
public class operationSub implements operation{

    @Override
    public int doOperation (int num1, int num2) {

        return num1 - num2;
    }
}
4.4calculator计算器类(承上启下类)
package com.wshy.testdemo.StrategyDemo;

/**
 * 计算器类,承上启下
 * @author wshy
 * @data 2020/7/11
 **/
public class calculator {

    //定义一个operation变量
    private operation operation;

    //operation变量的set方法
    public void setOperation(operation op){
        this.operation = op;
    }

    //抛出一个计算方法
    public int doOperation(int num1,int num2){
        return this.operation.doOperation (num1,num2);
    }

    //测试使用
    public static void main (String[] args) {
        calculator calcul = new calculator ();
        calcul.setOperation (new operationAdd ());
        int result = calcul.doOperation (1,2);
        System.out.println ("result1 = " + result);

        calcul.setOperation (new operationSub ());
        int result2 = calcul.doOperation (1,2);
        System.out.println ("result2 = " + result2);
    }

}

码字不易,请老铁留个👍,谢谢!

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值