原来策略模式和模板方法模式这么简单

1、策略模式

1.1、定义

  1. 概念:策略模式(Strategy Pattern)定义了一些列的算法,并将每个算法封装起来,而且它们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化
  2. 简单理解:现在要做一件事,分为多个步骤,大致流程是差不多的,但是中间的某个步骤执行流程不一样,那能不能让中间这个步骤可以灵活的变动呢?是可以的,我们中间这个步骤就传递一种策略或者算法进去即可

需要注意的是:

设计模式严格意义上来讲不是知识点,而是一种编程思想,是用来解决一些共性问题,这类问题产生的原因差不多,然后设计模式提出一种统一的解决方案

1.2、举例理解

开车从家去上班

public void driveCar(){
    //1、上车
    //2、系安全带
    //3、点火启动
    //4、掌握方向盘开车
    //5、抵达目的地停车
}

上面流程大致相同,不管是你还是你老婆开车去上班,都是一样的流程,可能唯一区别在于第4步,你可能按照A路线开车过去,但是你老婆可能按照B路线开车过去,路线是有多种的,那么你的代码大概率会这样写:

public void driveCar(){
    //1、上车
    //2、系安全带
    //3、点火启动
    //4、掌握方向盘开车
    	if(A路线){
            //...
        }else if(B路线){
            //...
        }else if(C路线){
            //...
        }
    //5、抵达目的地停车
}

这样写看上去没什么问题,也可以运行,那以后有其他路线时,我们就得再继续加else if了,最后可读性大大降低了,可维护性可扩展性都大大降低了

那我们接下来我们这样改造一下:将else抽取出来成为一个类中的一个方法,有多少个else就写多少个类和方法,形成一些策略,最后变成这样了:

public void driveCar(){
   //1、上车
   //2、系安全带
   //3、点火启动
   //4、掌握方向盘开车
   	执行策略,调用方法执行就可以了
   //5、抵达目的地停车
}

1.3、实操代码

我们就以开车上班工作的场景为例,大概思路如下:

在这里插入图片描述

好,开始编码,先定义一个流程出来:

package cn.itsource.strategy;

/**
 * 开车上班工作,定义一个流程
 * @author wujiangbo
 */
public class GoToWork {

    public void beginWork(Strategy strategy){
        //执行流程:第1步:上车
        System.out.println("执行流程:第1步:上车");
        //执行流程:第2步:系安全带
        System.out.println("执行流程:第2步:系安全带");
        //执行流程:第3步:点火启动
        System.out.println("执行流程:第3步:点火启动");

        //执行流程:第4步:掌握方向盘开车
        System.out.println("执行流程:第4步:掌握方向盘开车");
        strategy.drive();

        //执行流程:第5步:抵达目的地停车
        System.out.println("执行流程:第5步:抵达目的地停车");
    }
}

然后定义策略规则接口,规定必须有开车这个动作:

package cn.itsource.strategy;

/**
 * 策略规则接口
 * @author wujiangbo
 * @date 2022-09-14 13:13
 */
public interface Strategy {

    //开车动作的方法
    void drive();
}

那具体怎么开,我不管,所以可以用多种实现,比如:

  • 老公开车喜欢用百度地图导航,所以是A路线
  • 老婆开车喜欢用高德地图导航,所以是B路线

所以就有下面两个实现类了:

package cn.itsource.strategy;

/**
 * 百度地图规划的路线实现
 * @author wujiangbo
 */
public class StrategyBaidu implements Strategy{

    @Override
    public void drive() {
        System.out.println("百度地图路线:家 - 武昌火车站 - 汉口火车站 - 武汉火车站 - 公司");
    }
}
package cn.itsource.strategy;

/**
 * 高德地图规划的路线实现
 * @author wujiangbo
 */
public class StrategyGaode implements Strategy {

    @Override
    public void drive() {
        System.out.println("高德地图路线:家 - 武汉火车站 - 汉口火车站 - 武昌火车站 - 公司");
    }
}

好,我们开始测试:

package cn.itsource.strategy;

/**
 * 测试类
 * @author wujiangbo
 */
public class Test001 {

    public static void main(String[] args){
        GoToWork goToWork = new GoToWork();
        //Strategy strategy = new StrategyBaidu();
        Strategy strategy = new StrategyGaode();
        goToWork.beginWork(strategy);
    }
}

运行结果:

执行流程:第1步:上车
执行流程:第2步:系安全带
执行流程:第3步:点火启动
执行流程:第4步:掌握方向盘开车
百度地图路线:家 - 武昌火车站 - 汉口火车站 - 武汉火车站 - 公司
执行流程:第5步:抵达目的地停车

或者:

执行流程:第1步:上车
执行流程:第2步:系安全带
执行流程:第3步:点火启动
执行流程:第4步:掌握方向盘开车
高德地图路线:家 - 武汉火车站 - 汉口火车站 - 武昌火车站 - 公司
执行流程:第5步:抵达目的地停车

分析:

此时调用beginWork方法时,传不同的策略/算法进去,那开车这个过程就不一样了,但是整个大致的流程还是那5个步骤,不会改变,所以策略模式更加关注的是整个流程中的某一个流程的变动,而不会关注整个流程

1.4、使用场景

  • 针对同一类型的问题多种处理
  • 需要安全的封装多种同一类型的操作,对客户隐藏具体实现
  • 出现同一抽象类有多个子类,而又需要使用 if-else 或者switch-case 来选择具体子类时

1.5、优缺点

1、优点

  • 结构清晰明了,使用简单直观,干掉了大量的 if else
  • 耦合度相对较低,扩展方便
  • 操作封装更彻底,数据更加安全

2、缺点

  • 随着策略的增加,类会变得越来越多,容易造成类爆炸
  • 使用者必须知道所有策略类,并自行解决使用哪个策略

2、模板方法模式

2.1、定义

  1. 概念:在模板方法模式中将实现功能的每一个步骤所对应的方法称为基本方法,而将调用这些基本方法同时定义基本方法的执行次序的方法称为模板方法
  2. 模板方法模式将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果

2.2、举例理解

还是以上面开车从家去上班为例说明:

public class GoToWork{
    //1、上车
    //2、系安全带
    //3、点火启动
    //4、掌握方向盘开车
    //5、抵达目的地停车
}

用模板方法模式的话,我们就把上面5个步骤固定下来了,顺序不能变,所有人都得按照这个步骤开车去上班了,所以我们需要这样做:

  1. 写一个抽象类,将所有步骤先定义好
  2. 然后还需要规定好顺序,相当于定义了这么一套模板/框架/轮廓出来了

按照这个思路我们开始写代码

2.3、实操代码

我们就以开车上班工作的场景为例,大概思路如下:

在这里插入图片描述

好,开始编码,我们先定义一个模板/流程/框架出来:

package cn.itsource.template;

/**
 * @desc 定义开车去上班整个模板/流程/框架
 * @author wujiangbo(weixin:wjb1134135987)
 */
public abstract class GoToWork {

    //定义【模板/框架】的执行顺序
    public void goToWorkOrder(){
        this.shangche();
        this.anquandai();
        this.qidong();
        this.kaiche();
        this.tingche();
    }

    //第1步:上车
    protected abstract void shangche();

    //第2步:系安全带
    protected abstract void anquandai();

    //第3步:点火启动
    protected abstract void qidong();

    //第4步:掌握方向盘开车
    protected abstract void kaiche();

    //第5步:抵达目的地停车
    protected abstract void tingche();
}

整体的框架出来之后,所有人开车去上班,就都得按照这个流程去上班了,下面有张三和李四两个人要开车去上班了,都必须实现这个规范,实现类如下:

package cn.itsource.template;

/**
 * @desc 张三开车去上班
 * @author wujiangbo(weixin : wjb1134135987)
 */
public class ZhangsanGoToWork extends GoToWork{
    @Override
    protected void shangche() {
        System.out.println("张三上车:从副驾驶座爬上去的");
    }

    @Override
    protected void anquandai() {
        System.out.println("张三系安全带");
    }

    @Override
    protected void qidong() {
        System.out.println("张三启动汽车:用钥匙插入启动");
    }

    @Override
    protected void kaiche() {
        System.out.println("张三开车去上班:用百度地图导航");
    }

    @Override
    protected void tingche() {
        System.out.println("张三抵达目的地停车:喜欢停马路边");
    }
}
package cn.itsource.template;

/**
 * @desc 李四开车去上班
 * @author wujiangbo(weixin : wjb1134135987)
 */
public class LisiGoToWork extends GoToWork{

    @Override
    protected void shangche() {
        System.out.println("李四上车:从驾驶座爬上去的");
    }

    @Override
    protected void anquandai() {
        System.out.println("李四系安全带");
    }

    @Override
    protected void qidong() {
        System.out.println("李四启动汽车:无钥匙一键启动");
    }

    @Override
    protected void kaiche() {
        System.out.println("李四开车去上班:用高德地图导航");
    }

    @Override
    protected void tingche() {
        System.out.println("李四抵达目的地停车:喜欢停停车场");
    }
}

然后我们开始写测试类,正式去上班:

package cn.itsource.template;

/**
 * @desc 测试类
 * @author wujiangbo(weixin : wjb1134135987)
 */
public class TemplateTest {

    public static void main(String[] args){
        GoToWork goToWork = new ZhangsanGoToWork();
        //GoToWork goToWork = new LisiGoToWork();
        goToWork.goToWorkOrder();
    }
}

运行结果:

张三上车:从副驾驶座爬上去的
张三系安全带
张三启动汽车:用钥匙插入启动
张三开车去上班:用百度地图导航
张三抵达目的地停车:喜欢停马路边

或者是:

李四上车:从驾驶座爬上去的
李四系安全带
李四启动汽车:无钥匙一键启动
李四开车去上班:用高德地图导航
李四抵达目的地停车:喜欢停停车场

这样就达到了目的,不管谁开车,都要遵守统一的模板/框架,顺序不能改变,具体实现细节,可以自由扩展

2.4、使用场景

  1. 有多个子类共有的方法,且逻辑相同
  2. 重要的、复杂的方法,可以考虑作为模板方法

注意事项: 为防止恶意操作,一般模板方法都加上 final 关键词

2.5、优缺点

1、优点

  1. 封装不变部分,扩展可变部分
  2. 提取公共代码,便于维护
  3. 行为由父类控制,子类负责具体实现

2、缺点

  1. 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
  2. 耦合性较高,子类无法影响父类公用模块代码

3、总结

策略模式和模板方法模式看起来非常的相似,那有什么区别呢?

  1. 策略模式关注的是整体流程中的某一个环节/算法/策略(局部)
  2. 而模板方法模式更多的关注的定义一整套固定的模板/框架/轮廓(整体)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小学生波波

感谢您的厚爱与支持

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

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

打赏作者

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

抵扣说明:

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

余额充值