策略模式——运筹帷幄

一、定义

定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。

二、抛砖引玉

这个通俗易懂的小栗子,原版出自《设计模式之禅(第二版)》。

三国情景再现:
诸葛亮在刘备去东吴招亲之前,特授予伴郎赵云三个锦囊,说是按天机拆开解决棘手问题。

这三个妙计分别是:找乔国老帮忙(也就是走后门了),求吴国太放行(开绿灯)以及孙夫人断后。

接下来我们模拟这三个妙计的使用场景。

//妙计接口:
public interface Strategy {
    void operate();
}


//妙计具体实现(一)——乔国老开后门
public class BackDoor implements Strategy {
    @Override
    public void operate() {
        System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
    }
}
//妙计具体实现(二)——求吴国太放行(开绿灯)
public class GivenGreenLight implements Strategy {
    @Override
    public void operate() {
        System.out.println("求吴国太开绿灯,放行");
    }
}

//妙计具体实现(三)——孙夫人断后
public class BlockEnemy implements Strategy {
    @Override
    public void operate() {
        System.out.println("孙夫人断后,挡住追兵");
    }
}

//装锦囊的袋子
public class Context {
    private Strategy strategy;
    public Context(Strategy strategy){
        this.strategy = strategy;
    }

    //使用计谋
    public void operate(){
        this.strategy.operate();
    }
}

//赵云
public class ZhaoYun {
    public static void main(String[] args) {
        Context context = null;
        System.out.println("---刚刚到吴国的时候拆第一个锦囊");
        context = new Context(new BackDoor());
        context.operate();

        System.out.println("---刘备乐不思蜀,拆开第二个");

        context = new Context(new GivenGreenLight());
        context.operate();

        System.out.println("---孙权的小兵追了,拆第三个");
        context  = new Context(new BlockEnemy());
        context.operate();
    }
}

//最后运行结果

---刚刚到吴国的时候拆第一个锦囊
找乔国老帮忙,让吴国太给孙权施加压力
---刘备乐不思蜀,拆开第二个
求吴国太开绿灯,放行
---孙权的小兵追了,拆第三个
孙夫人断后,挡住追兵

栗子很简单,也很明了。不再做过多解释,倘若看不太懂,说明你需要补充一下java基础。

策略模式属于行为型模式,在实际开发中,我们要分析一个事物哪些行为是不可变的,哪些行为是可变的。可变行为的开发,日后开发要做到易维护、易扩展。

前人为我们总结出了策略模式,将可变行为抽象,接口化(符合迪米特原则)行为分类定义接口(符合接口分离原则),之后对应实现具体的行为,日后需要扩展时就新建一个类实现行为接口(符合开闭原则)。

三、第二个栗子:

接下来再看一个鸭子的栗子,鸭子的种类很多,有的会飞,飞得高飞得久(所谓飞得好),反之就是飞得差,有的甚至不会飞,就不需要这个行为;有的叫声是咕咕,有的叫声嘎嘎。

首先我们把飞和叫声分别抽象

飞行为抽象

public interface FlyBehavior {
    void fly();
}

//飞得好
public class GoodFlyBehavior implements FlyBehavior {
    private GoodFlyBehavior(){

    }
    public  static  GoodFlyBehavior getInstantiation(){
        return new GoodFlyBehavior();
    }
    @Override
    public void fly() {
        System.out.println("good-----fly");
    }
} 

//飞得差
public class BadFlyBehavior implements FlyBehavior{
    private BadFlyBehavior(){

    }
    //忽略我这里用了反射
    public static BadFlyBehavior getInstantiation(){
        return new BadFlyBehavior();
    }
    @Override
    public void fly() {
        System.out.println("bad----fly");
    }
}



叫声行为抽象:

public interface QuackBehavior {
    void quack();
}

public class GaGaQuackBehavior implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("gaga---quack");
    }
}

public class GuGuQuackBehavior implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("gugu---quack");
    }
}

接下来我们开始造鸭子了

//首先造一只抽象的鸭子
public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    public Duck(){

    }

    public void fly(){
        if (flyBehavior!=null){
            flyBehavior.fly();
        }
    }

    public void quack(){
        if (quackBehavior!=null){
            quackBehavior.quack();
        }
    }
    public abstract void eat();

    public void swim(){
        System.out.println("小鸭子swim--");
    }
}

//绿头鸭飞得好,嘎嘎叫
public class GreenHeadDuck extends Duck {

    public GreenHeadDuck(){
        flyBehavior = GoodFlyBehavior.getInstantiation();
        quackBehavior = new GaGaQuackBehavior();
    }
    @Override
    public void eat() {
        System.out.println("吃鱼");
    }


}
//红头鸭飞得差,咕咕叫
public class RedHeadDuck extends Duck {

    public RedHeadDuck(){
        flyBehavior = BadFlyBehavior.getInstantiation();
        quackBehavior = new GuGuQuackBehavior();
    }
    @Override
    public void eat() {
        System.out.println("吃鱼---");
    }
}

//蓝头鸦,不会飞,咕咕叫
public class BlueHeadDuck extends Duck {
    public BlueHeadDuck(){
        quackBehavior = new GuGuQuackBehavior();
    }
    @Override
    public void eat() {
        System.out.println("坐着吃");
    }
}


//测试:
public class test1 {
    public static void main(String[] args) {
        Duck green = new GreenHeadDuck();
        green.fly();
        green.quack();
        System.out.println("---------------------------");
        Duck red = new RedHeadDuck();
        red.fly();
        red.quack();
        System.out.println("---------------------------");
        Duck blue = new BlueHeadDuck();
        blue.fly();// 虽然调了fly,但是没打印出什么,看方法逻辑上此方法是空的。
        blue.quack();
    }
}

--------------绿头鸭-------------
good-----fly
gaga---quack
--------------红头鸭-------------
bad----fly
gugu---quack
--------------蓝头鸭-------------
gugu---quack

可以看得出策略模式使用的就是面向对象的继承和多态机制。

策略模式的优点:

  1. 策略可以自由切换
  2. 避免使用多重条件判断
  3. 扩展性好

缺点:

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

使用场景:

  1. 多个类只有在算法或者行为上稍有不同的场景
  2. 算法需要自由切换的场景
  3. 需要屏蔽算法规则的场景

四、再来举个计算加减的栗子:

同样出自《设计模式之禅(第二版)》的改编。

一个计算器,有两个功能,两个数相加,两个数相减。

public interface Calculator {
    int exec(int a,int b);
}

public class Add implements Calculator {
    @Override
    public int exec(int a, int b) {
        return a+b;
    }
}

public class Sub implements Calculator {
    @Override
    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){
        return  this.cal.exec(a,b);
    }

}

public class test {
    public static void main(String[] args) {
        Context context = new Context(new Add());
        int result = context.exec(10,10);
        System.out.println(result);

        Context context1 = new Context(new Sub());
        int result1 = context1.exec(20,10);
        System.out.println(result1);
    }
}

来个高端的操作:

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 add_result = Calculator.ADD.exec(10,20);
        int sub_result = Calculator.SUB.exec(20,10);

        System.out.println(add_result);
        System.out.println(sub_result);
    }
}

是不是感觉很清爽,这就叫策略枚举

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

需要注意:

如果系统中的一个策略家族的具体策略数量超过了4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就会成为一个烫手山芋。

策略模式的精华远不止这些,还待实际开发中运用和体会。

源码地址:
https://gitee.com/stefanpy/DesignPattern

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容

  • 之一预感 只差一步就成功了, 当谨慎的思维成为圆形的句号。 市场的调查有了最初的结果, 亲友的鼓励是最大的的推手,...
    闲不语阅读 199评论 1 2
  • 花架上除了红线还有一张卡片,卡片上写着,请跟着红线延伸的地方前进。 简宁转身想问叶深,却发现身后空空如也,哪有半个...
    星玥小屋上班有事留言阅读 279评论 0 0
  • 这次从老家回北京,因为从荆州买不到座票,只能从岳阳买到北京西站才有座位,所以在网上,早早的买了趟Z2的列车到北京。...
    仲翔阅读 459评论 0 4
  • 今天是主动直立行走且户外活动的第一天,虽然狂风大作,发型百变,最终成了鸡窝的造型,有点儿小狼狈,但值得纪念。 01...
    翊M阅读 338评论 0 0