12-策略模式

1.定义

策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern)。其定义如下:
Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)


2.类图

这里写图片描述


3.代码

/**
* 抽象的策略角色
*/
public interface Strategy {
    //策略模式的运算法则
    public void doSomething();
}
/**
* 具体策略角色
*/
public class ConcreteStrategy1 implements Strategy {
    public void doSomething() {
        System.out.println("具体策略1的运算法则");
    }
}

public class ConcreteStrategy2 implements Strategy {
    public void doSomething() {
        System.out.println("具体策略2的运算法则");
    }
}
/**
* 封装角色
*/
public class Context {
    //抽象策略
    private Strategy strategy = null;
    //构造函数设置具体策略
    public Context(Strategy _strategy){
        this.strategy = _strategy;
    }
    //封装后的策略方法
    public void doAnythinig(){
        this.strategy.doSomething();
    }
}
/**
* 高层模块
*/
public class Client {
    public static void main(String[] args) {
        //声明一个具体的策略
        Strategy strategy = new ConcreteStrategy1();
        //声明上下文对象
        Context context = new Context(strategy);
        //执行封装后的方法
        context.doAnythinig();
    }
}

就是采用了面向对象的继承和多态机制,其他没什么玄机。


4.优点

  • 算法可以自由切换

这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装,保证对外提供“可自由切换”的策略。

  • 避免使用多重条件判断

如果没有策略模式,我们想想看会是什么样子?一个策略家族有5个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。

  • 扩展性良好

这甚至都不用说是它的优点,因为它太明显了。在现有的系统中增加一个策略太容易了,只要实现接口就可以了,其他都不用修改,类似于一个可反复拆卸的插件,这大大地符合了OCP原则。


5.缺点

  • 策略类数量增多

每一个策略都是一个类,复用的可能性很小,类数量增多。

  • 所有的策略类都需要对外暴露

上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。


6.使用场景

  • 多个类只有在算法或行为上稍有不同的场景。
  • 算法需要自由切换的场景。
    例如,算法的选择是由使用者决定的,或者算法始终在进化,特别是一些站在技术前沿的行业,连业务专家都无法给你保证这样的系统规则能够存在多长时间,在这种情况下策略模式是你最好的助手。
  • 需要屏蔽算法规则的场景。
    现在的科技发展得很快,人脑的记忆是有限的(就目前来说是有限的),太多的算法你只要知道一个名字就可以了,传递相关的数字进来,反馈一个运算结果,万事大吉。

7.扩展

先给出一道小学的题目:输入3个参数,进行加减法运算,参数中两个是int型的,剩下的一个参数是String型的,只有“+”、“-”两个符号可以选择

第一种方案:

/**
* 最直接的加减法
*/
public class Calculator {
    //加符号
    private final static String ADD_SYMBOL = "+";
    //减符号
    private final static String SUB_SYMBOL = "-";
    public int exec(int a,int b,String symbol){
        int result =0;
        if(symbol.equals(ADD_SYMBOL)){
            result = this.add(a, b);
        }else if(symbol.equals(SUB_SYMBOL)){
            result = this.sub(a, b);
        }
        return result;
    }
    //加法运算
    private int add(int a,int b){
        return a+b;
    }
    //减法运算
    private int sub(int a,int b){
        return a-b;
    }
}


/**
* 场景类
*/
public class Client {
    public static void main(String[] args) {
        //输入的两个参数是数字
        int a = Integer.parseInt(args[0]);
        String symbol = args[1]; //符号
        int b = Integer.parseInt(args[2]);
        System.out.println("输入的参数为:"+Arrays.toString(args));
        //生成一个运算器
        Calculator cal = new Calculator();
        System.out.println("运行结果为:"+a + symbol + b + "=" + cal.exec(a, b, symbol));
    }
}

输入的参数为:[100, +, 200]
运行结果为:100+200=300
第二种方案:

/**
* 简化算法
*/
public class Calculator {
    //加符号
    private final static String ADD_SYMBOL = "+";
    //减符号
    private final static String SUB_SYMBOL = "-";
    public int exec(int a,int b,String symbol){
        return symbol.equals(ADD_SYMBOL)?a+b:a-b;
    }
}
第三种方案:

/**
* 引入策略模式
*/
interface Calculator {
    public int exec(int a,int b);
}

/**
* 具体策略
*/
public class Add implements Calculator {
    //加法运算
    public int exec(int a, int b) {
        return a+b;
    }
}
public class Sub implements Calculator {
    //减法运算
    public int exec(int a, int b) {
        return a-b;
    }
}

/**
* 上下文
*/
public class Context {
    private Calculator cal = null;
    public Context(Calculator _cal){
        this.cal = _cal;
    }
    public int exec(int a,int b,String symbol){
        return this.cal.exec(a, b);
    }
}

/**
* 场景类
*/
public class Client {
    //加符号
    public final static String ADD_SYMBOL = "+";
    //减符号
    public final static String SUB_SYMBOL = "-";
    public static void main(String[] args) {
        //输入的两个参数是数字
        int a = Integer.parseInt(args[0]);
        String symbol = args[1]; //符号
        int b = Integer.parseInt(args[2]);
        System.out.println("输入的参数为:"+Arrays.toString(args));
        //上下文
        Context context = null;
        //判断初始化哪一个策略
        if(symbol.equals(ADD_SYMBOL)){
            context = new Context(new Add());
        }else if(symbol.equals(SUB_SYMBOL)){
            context = new Context(new Sub());
        }
        System.out.println("运行结果为:"+a+symbol+b+"="+context.exec(a,b,symbol));
    }
}
第四种方案:

/**
* 策略枚举
*/
public enum Calculator {
    //加法运算
    ADD("+"){
        public int exec(int a,int b){
            return a+b;
        }
    },
    //减法运算
    SUB("-"){
        public int exec(int a,int b){
            return a - b;
        }
    };
    String value = "";
    //定义成员值类型
    private Calculator(String _value){
        this.value = _value;
    }
    //获得枚举成员的值
    public String getValue(){
        return this.value;
    }
    //声明一个抽象函数
    public abstract int exec(int a,int b);
}

/**
* 场景类
*/
public class Client {
    public static void main(String[] args) {
    //输入的两个参数是数字
    int a = Integer.parseInt(args[0]);
    String symbol = args[1]; //符号
    int b = Integer.parseInt(args[2]);
    System.out.println("输入的参数为:"+Arrays.toString(args));
    System.out.println("运行结果为:"+a+symbol+b+"="+Calculator.ADD.exec(a,b));
    }
}

注意 策略枚举是一个非常优秀和方便的模式,但是它受枚举类型的限制,每个枚举项都是public、final、static的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值