了解策略模式--通过详细代码与例子

了解策略模式–通过详细代码与例子

应用场景

完成一项任务,往往可以有多种策略可以实现,要想灵活选择策略和添加新策略,可以使用策略模式。
比如从数组中查找某个值的任务,我们可以从头到尾遍历查找,可以从尾到头遍历查找,如果数组有序,还可以用二分法查找。不同的数组可以采用不同的策略来实现查找任务,我们还想让数组的查找方式能设置与修改,这时候就可以用上策略模式,把不同的查找方法封装成独立的类,数组可以自行选择使用哪种查找策略。

定义

策略模式定义了一系列策略,每一个策略都封装起来,并让他们可以相互转换,使用策略的客户可以任意选择策略,策略的变化独立于使用策略的客户

例子

就用上面的数组查值为例子,任务是从数组中查找某个值,策略有从头到尾查找和从尾到头查找,策略独立于数组类,下面是UML类图

在这里插入图片描述
NumberArray为抽象类,内部属性有protected的arr 和searchAlgorithm ,arr是Number数组,存放数据,searchAlgorithm是接口ISearchAlgorithm类型,后者定义了find方法用于从数据中查找某个数。实现了该接口的类有LeftSearchAlgorithm和RightSearchAlgorithm,分别实现了从头到尾和从尾到头查找数值的策略。NumberArray的子类有IntegerArray和DoubleArray,分别是整数数组和double小数数组,下面是代码
ISearchAlgorithm.java

package priv.mxz.design_pattern.strategy_pattern;

interface ISearchAlgorithm {

    int find(Number[] arr, Number num);
}

class LeftSearchAlgorithm implements ISearchAlgorithm {

    @Override
    public int find(Number[] arr,Number num) {
        if (arr==null)
            return -1;
        for (int i=0; i<arr.length; i++){
            if (arr[i].equals(num))
                return i;
        }
        return -1;
    }

    @Override
    public String toString() {
        return "strategy_pattern.LeftSearchAlgorithm";
    }
}

class RightSearchAlgorithm implements ISearchAlgorithm {

    @Override
    public int find(Number[] arr, Number num) {
        if (arr==null)
            return -1;
        for (int i=arr.length-1; i>=0; i--){
            if (arr[i].equals(num))
                return i;
        }
        return -1;
    }

    @Override
    public String toString() {
        return "strategy_pattern.RightSearchAlgorithm";
    }
}


NumberArray.java

package priv.mxz.design_pattern.strategy_pattern;

import java.util.Arrays;

abstract class NumberArray {
    protected Number[] arr;
    protected ISearchAlgorithm searchAlgorithm;
    public NumberArray(Number[] arr){
        this.arr=arr;
    }

    int find(Number num){
        return searchAlgorithm.find(arr,num);
    }

    public void setSearchAlgorithm(ISearchAlgorithm searchAlgorithm){
        this.searchAlgorithm=searchAlgorithm;
    }

    @Override
    public String toString() {
        return "strategy_pattern.NumberArray{" +
                "arr=" + Arrays.toString(arr) +
                ", searchAlgorithm=" + searchAlgorithm +
                '}';
    }
}

class IntegerArray extends NumberArray{

    public IntegerArray(Integer[] arr,ISearchAlgorithm searchAlgorithm){
        super(arr);
        setSearchAlgorithm(searchAlgorithm);
    }


}

class DoubleArray extends NumberArray{
    public DoubleArray(Double[] arr,ISearchAlgorithm searchAlgorithm){
        super(arr);
        setSearchAlgorithm(searchAlgorithm);
    }

}


StrategyPattern.java

package priv.mxz.design_pattern.strategy_pattern;

public class StrategyPattern {

    public static void main(String[] args) {

        IntegerArray integerArray=new IntegerArray(
                new Integer[]{1,2,3,3,2,1},
                new LeftSearchAlgorithm());
        DoubleArray doubleArray=new DoubleArray(
                new Double[]{1.0,2.0,3.0,3.0,2.0,1.0},
                new LeftSearchAlgorithm());

        System.out.println("strategy_pattern.IntegerArray: "+integerArray);
        System.out.println("strategy_pattern.IntegerArray found 2 at index "+integerArray.find(2));

        System.out.println("Change integerArray search algorithm to right");
        integerArray.setSearchAlgorithm(new RightSearchAlgorithm());
        System.out.println("strategy_pattern.IntegerArray: "+integerArray);
        System.out.println("strategy_pattern.IntegerArray found 2 at index "+integerArray.find(2));

        System.out.println("strategy_pattern.DoubleArray: "+doubleArray);
        System.out.println("strategy_pattern.DoubleArray found 2 at index "+doubleArray.find(2.0));


    }
}

StrategyPattern类中有执行入口,main函数中可以看到,初始化时integerArray和doubleArray的查找策略都设置为从头到尾查找,然后打印integerArray的信息和调用find方法查找2的位置,结果为1,即数组中第一个2的index。随后我们通过integerArray.setSearchAlgorithm(new RightSearchAlgorithm());修改integerArray的查找策略为从尾到头,打印信息和调用find方法查找2的位置,结果为4,即数组中第二个2的index。最后打印doubleArray的信息和调用find方法,发现doubleArray的查找策略不收integerArray的影响,不会因为IntegerArray策略而改变。
运行输出结果如下

strategy_pattern.IntegerArray: strategy_pattern.NumberArray{arr=[1, 2, 3, 3, 2, 1], searchAlgorithm=strategy_pattern.LeftSearchAlgorithm}
strategy_pattern.IntegerArray found 2 at index 1
Change integerArray search algorithm to right
strategy_pattern.IntegerArray: strategy_pattern.NumberArray{arr=[1, 2, 3, 3, 2, 1], searchAlgorithm=strategy_pattern.RightSearchAlgorithm}
strategy_pattern.IntegerArray found 2 at index 4
strategy_pattern.DoubleArray: strategy_pattern.NumberArray{arr=[1.0, 2.0, 3.0, 3.0, 2.0, 1.0], searchAlgorithm=strategy_pattern.LeftSearchAlgorithm}
strategy_pattern.DoubleArray found 2 at index 1

优缺点

在上述的例子中,如果不使用策略模式,采用硬编码把find函数的实现写入NumberArray或者它的子类中,则会有下面的问题

  1. 如果在NumberArray中实现find方法,那么所有的子类的find方法都默认是同一个find策略,我们假设使用从头到尾策略,除非子类重写覆盖find方法,那么如果有多个子类要使用从尾到头策略,这些子类都需要重写一份从尾到头策略,造成代码的重复。比如DoubleArray自己实现了从尾到头策略,此时若LongArray也要实现从尾到头策略,就需要把DoubleArray中的复制过来做修改,这样就造成了多份类似甚至相同的代码,如果NumberArray不实现find方法,则每个子类都要写自己的find方法,代码的重复度更高。
  2. 如果子类要修改查找策略,只能修改源代码,如果要实现在运行时改变查找策略,也要修改源代码,添加if else判断来确定查找策略,或者分别定义find和rfind。而是用策略模式只需要调用setSearchAlgorithm方法

所以,使用策略模式的优点如下:

  1. 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择策略,也可以灵活地增加新策略。
  2. 策略模式提供了管理相关策略的办法。(例子中的setSearchAlgorithm)
  3. 策略模式提供了可以替换继承关系的办法。
  4. 使用策略模式可以避免使用多重条件转移语句。

策略模式的缺点如下:

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  2. 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值