设计模式之策略模式

设计模式 专栏收录该内容
15 篇文章 0 订阅

1.刘备江东娶妻,赵云他容易吗?

大家还记得江东取妻的故事吗?
孙权有个妹妹——孙尚香,准备招刘备做女婿,然后孙权想办法把 刘备软禁起来,孙权的想法还是很单纯的嘛,就是不让你刘备回西川,然后我东吴想干啥就 干啥,夺荆州,吞西川也不是不可能的。东吴的想法是好的,无奈中间多了智谋无敌的诸葛 亮,他早就预测了东吴有此招数,于是在刘备去东吴招亲之前,特授以伴郎赵云三个锦囊, 说是按天机拆开解决棘手问题。
这三个妙计分别是:1.,找乔国老帮忙(也就是走后门了);2.求吴国太放行(诉苦);3.孙 夫人断后。想想看,这三个计谋有什么相似之处,他们都是告诉赵云要怎么执行,也就是说这三个计谋都 有一个方法是执行,具体执行什么内容,每个计谋当然不同了,分析到这里,我们是不是就 有这样一个设计思路:三个妙计应该实现的是同一个接口?聪明!是的。

2.代码实现

这里写图片描述

(1) 三个计谋

这是非常简单的类图,在这个场景中的三个主要角色都已经有了,每个妙计都提供了一 个可执行的方法,我们先来看接口。
妙计接口代码如下:
public interface IStrategy {
//每个锦囊妙计都是一个可执行的算法
public void operate();
}
接口很简单,定义了一个方法operate,每个妙计都是可执行的,否则那叫什么妙计。
第一个妙计——乔国老开后门代码如下:
public class BackDoor implements IStrategy {
public void operate() {
System.out.println(“找乔国老帮忙,让吴国太给孙权施加压力”);
}
}
第二个妙计——找吴国太哭诉,给自己开绿灯
public class GivenGreenLight implements IStrategy {
public void operate() { System.out.println(“求吴国太开绿灯,放行!”);
}
}
第三个妙计——逃跑的时候,让孙夫人断后,谁来砍谁
public class BlockEnemy implements IStrategy {
public void operate() { System.out.println(“孙夫人断后,挡住追兵”);
}
}
三个妙计都有了,那还缺少两个配角:
第一,妙计肯定要放到一个地方吧,这么重要的东西要保管呀,也就是承装妙计的锦囊,所以俗称锦囊妙计嘛;
第二,这些 妙计都要有一个执行人吧,是谁?当然是赵云了,妙计是小亮给的,执行者是赵云。赵云就是一个干活的人,从锦囊中取出妙计,执行,然后获胜。过程非常清晰,我们把完整的过程设计出来.

(2)锦囊

在类图中增加了一个Context封装类(也就是锦囊),其作用是承装三个策略,方便赵云使用,我们来看Context代码:
public class Context {
//构造函数,你要使用哪个妙计
private IStrategy straegy;
public Context(IStrategy strategy){
this.straegy = strategy;
}
//使用计谋了,看我出招了
public void operate(){
this.straegy.operate();
}
}
通过构造函数把策略传递进来,然后用operate()方法来执行相关的策略方法。三个妙计有了,锦囊也有了,然后就是赵云雄赳赳地揣着三个锦囊,拉着已步入老年行列的、还想着娶纯情少女的刘老爷子去入赘了。嗨,还别说,小亮同志的三个妙计还真是不错!

我们的赵云登场了

public class ZhaoYun {
    //赵云出场了,他根据诸葛亮给他的交代,依次拆开妙计
    public static void main(String[] args) {
    Context context;
    //刚刚到吴国的时候拆第一个
    System.out.println("---刚刚到吴国的时候拆第一个---"); context = new Context(new BackDoor()); //拿到妙计 context.operate();  //拆开执行
    System.out.println("\n\n\n\n\n\n\n\n");
    //刘备乐不思蜀了,拆第二个了
    System.out.println("---刘备乐不思蜀了,拆第二个了---");
    context = new Context(new GivenGreenLight());
    context.operate();  //执行了第二个锦囊
    System.out.println("\n\n\n\n\n\n\n\n");
    //孙权的小兵追来了,咋办?拆第三个
    System.out.println("---孙权的小兵追来了,咋办?拆第三个---");
    context = new Context(new BlockEnemy());
    context.operate();  //孙夫人退兵
    System.out.println("\n\n\n\n\n\n\n\n");
    }
}

我们来看看这段故事,运行结果如下:
—刚刚到吴国的时候拆第一个—
找乔国老帮忙,让吴国太给孙权施加压力
—刘备乐不思蜀了,拆第二个—
求吴国太开个绿灯,放行!
—孙权的小兵追来了,咋办?拆第三个—
孙夫人断后,挡住追兵
恩,不错,就这三招,搞得孙权是“赔了夫人又折兵”。那我们描述这个故事的过程就是策略模式。

总结

定义

策略模式:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
这个定义是非常明确、清晰的,“定义一组算法”,看看我们的三个计谋是不是三个算 法?“将每个算法都封装起来”,封装类Context不就是这个作用吗?“使它们可以互换”当然可 以互换了,都实现是相同的接口,那当然可以相互转化了。
策略模式就是这么简单,偷着乐吧,它就是采用了面向对象的继承和多态机制,其他没什么玄机。想想看,你真实的业务环境有这么简单吗?一个类实现多个接口很正常,你要有 火眼金睛看清楚哪个接口是抽象策略接口,哪些是和策略模式没有任何关系,这就是你作为 系统分析师的价值所在。

扩展

介绍一个策略枚举:
大家想想如何进行加减运算呢?

方法1:

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;
    }
}

方法2:

public int exec(int a,int b,String symbol){
    return symbol.equals(ADD_SYMBOL)?a+b:a-b;
}

方法3:(引入策略模式)

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 void client() {
    //输入的两个参数是数字
    int a = 1; 

    int b = 2; 
    Context context = null;
//判断初始化哪一个策略
if(symbol.equals("+")){
    context = new Context(new Add());
}else if(symbol.equals("-")){
    context = new Context(new Sub());
} 
    System.out.println("运行结果为:"+a+symbol+b+"="+context.exec(a,b,symb

}

策略枚举:

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 void client() {
    //输入的两个参数是数字
    int a = 1; 
    String symbol = args[1];    //符号
    int b = 2; 
    System.out.println("输入的参数为:+");
    System.out.println("运行结果为:"+a+"+"+b+"="+Calculator.ADD.exec(a,b));
}

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

结尾

好了,就讲到这里吧,策略模式是一个非常简单的模式。它在项目中使用得非常多,但它单独使用的地方就比 较少了,因为它有致命缺陷:所有的策略都需要暴露出去,这样才方便客户端决定使用哪一 个策略。例如,在例子中的赵云,实际上不知道使用哪个策略,他只知道拆第一个锦囊,而 不知道是BackDoor这个妙计。是的,诸葛亮已经在规定了在适当的场景下拆开指定的锦囊, 我们的策略模式只是实现了锦囊的管理,但是我们没有严格地定义“适当的场景”拆开“适当 的锦囊”,在实际项目中,我们一般通过工厂方法模式来实现策略类的声明,读者可以参考 混编模式。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
第1章 策略模式 刘备要到江东娶老婆了,走前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开解决棘手问题,嘿,还别说,真是解决了大问题,搞到最后是周瑜陪了夫人又折兵呀,那咱们先看看这个场景是什么样子的... 第2章 代理模式 什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代理人虽然不能干活,但是被代理的人能干活呀。 比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时这样的... 第3章 单例模式 这个模式是很有意思,而且比较简单,但是我还是要说因为它使用的是如此的广泛,如此的有人缘,单例就是单一、独苗的意思,那什么是独一份呢?你的思维是独一份,除此外还有什么不能山寨的呢?我们举个比较难复制的对象:皇帝。 中国的历史上很少出现两个皇帝并存的时期... 第4章 多例模式 这种情况有没有?有!大点声,有没有? 有!,是,确实有,就出现在明朝,那三国期间的算不算,不算,各自称帝,各有各的地盘,国号不同。大家还记得那首诗《石灰吟》吗?作者是谁?于谦,他是被谁杀死的?明英宗朱祁镇,对,就是那个在土木堡变中被瓦刺俘虏的皇帝,被俘虏后,他弟弟朱祁钰当上了皇帝,就是明景帝,估计当上皇帝后乐疯了,忘记把老哥朱祁镇削为太上皇了,我 Shit,在中国的历史上就这个时期是有 2 个皇帝,你说这期间的大臣多郁闷,两个皇帝耶,两个精神依附对象呀。 这个场景放到我们设计模式中就是叫有上限的多例模式 (没上限的多例模式太容易了,和你直接 new 一个对象没啥差别,不讨论)怎么实现呢,看我出招,先看类图...
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值