我的Java设计模式-策略模式

今天给大家说说田忌赛马的故事。如有雷同,纯属巧合!话说在战国时期,群雄割据,硝烟四起,茶余饭后还是少不了娱乐活动的,其中赛马是最火爆的。一天,孙膑看到田忌像个死鸡似的就知道肯定赛马又输给了齐威王,立马抓住田忌去跟齐威王再赛一场。

孙膑:“小忌啊,哥哥看着你心疼啊,哥哥出对策帮你赢一盘如何?”。

田忌听到之后高兴得飞起,瞪大了两只金鱼眼“Really?只要能赢,我赴汤蹈火,以身相许又如何~”。

孙膑心里一万个草泥马在奔腾,差点没噎死自己“滚一边去,我们这盘跟他show hand!”赛马开始,策略模式上场。此处应该有bgm“让我们红尘作伴活得潇潇洒洒 策马奔腾共享人世繁华...呀啊呀啊,呀啊啊啊啊啊啊~”

一、策略模式

定义

定义一组算法,将每一个算法封装起来,从而使它们可以相互切换。

特点

1)一组算法,那就是不同的策略。

2)这组算法都实现了相同的接口或者继承相同的抽象类,所以可以相互切换

UML

策略模式UML图.png

策略模式涉及到的角色有三个:

- 封装角色:上层访问策略的入口,它持有抽象策略角色的引用。

- 抽象策略角色:提供接口或者抽象类,定义策略组必须拥有的方法和属性。

- 具体策略角色:实现抽象策略,定义具体的算法逻辑。

二、实战

在跟齐威王比赛之前来分析下之前输掉比赛的“策略”,首先来看封装角色,代码如下:

public class Context {

    private Strategy strategy;

    /**
     * 传进的是一个具体的策略实例
     * @param strategy
     */
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 调用策略
     */
    public void contextInterface() {
        strategy.algorithmLogic();
    }

}

Context持有Strategy的引用,并且提供了调用策略的方法,很清晰。

再来抽象策略角色,定义了策略组的方法,代码如下:

public interface Strategy {

    public void algorithmLogic();

}

输掉比赛的“策略”也是一种策略,是具体策略角色类,来看代码:

public class ConcreteStrategyA implements Strategy{

    @Override
    public void algorithmLogic() {
        // 具体的算法逻辑(输了比赛)
        System.out.println("第一场:上等马vs上等马  第二场:中等马vs中等马  第三场:下等马vs下等马  赛果:输!");
    }
}

看到这里,孙膑一阵无语,惨不忍睹也得看结果的,客户端代码如下:

public class Client {

    public static void main(String[] args) {
        // 操控比赛,这场要输
        Context context = new Context(new ConcreteStrategyA());
        context.contextInterface();
    }
}

两句代码,传入具体策略对象,调用策略入口方法,运行结果如下:

第一场:上等马vs上等马 第二场:中等马vs中等马 第三场:下等马vs下等马 赛果:输!

田忌跟孙膑说:“膑哥,我怕!”,孙膑:“不用怕,哥哥在!”。

田忌找到齐威王“大王,我们再...再再来一盘,输了请吃饭”

瞅瞅孙膑出的策略,一睹军事家的风采,“赢”的具体策略类代码如下:

public class ConcreteStrategyB implements Strategy{
    @Override
    public void algorithmLogic() {
        // 赢
        System.out.println("第一场:下等马vs上等马  第二场:上等马vs中等马  第三场:中等马vs下等马  赛果:赢!");
    }
}

再来看客户端的代码:

public class Client {
    public static void main(String[] args) {

        // 操控比赛,这场要赢,哈哈哈
        Context context = new Context(new ConcreteStrategyB());
        context.contextInterface();
    }
}

运行结果如下:

第一场:下等马vs上等马 第二场:上等马vs中等马 第三场:中等马vs下等马 赛果:赢!

田忌拍烂手掌,重要的是今天晚饭有着落了,还要对膑哥哥以身相许的......

三、策略模式的优缺点

优点

1)良好的扩展性。增加一种策略,只要实现接口,写上具体逻辑就可以了。当旧策略不需要时,直接剔除就行。

2)良好的封装性。策略的入口封装在Context封装类中,客户端只要知道使用哪种策略就传哪种策略对象就可以了。

3)避免了像简单工厂模式这样的多重条件判断。

缺点

1)客户端必须了解策略组的各个策略,并且决定使用哪一个策略,也就是各个策略需要暴露给客户端。

2)如果策略增多,策略类的数量就会增加。

四、扩展

上面说到策略模式有一个缺点,就是所有的策略都必须暴露出去,让客户端自行选择策略使用。现在来改善这一缺陷,而改善这个缺陷需要跟简单工厂模式结合混编,继续往下看。

当然,军事家孙膑也会想到这一点,怎么可能会把自己的套路全都暴露给别人呢,那还怎么玩是吧。不过,历史上并没有说孙膑改善了这点,现在是我来改善这个缺陷,哈哈哈~

策略工厂

思考一个问题,策略暴露了,改善就是把策略隐藏起来,而工厂模式就有这个效果,客户端不需要知道策略具体是什么,只知道结果就好。OK,那么我们可以使用工厂模式把策略当做产品生成吗?答案是肯定的。策略模式的入口就在Context封装类,可以从这个角色做手脚。先看代码:

public class Context {

    private Strategy strategy;

    // 把创建策略放在封装角色内,客户端只需要知道结果
    public void factory(String strategyType) {
        if (strategyType.equals("WIN")) {
            strategy = new ConcreteStrategyB();
        } else if (strategyType.equals("LOSE")) {
            strategy = new ConcreteStrategyA();
        }
    }

    /**
     * 调用策略
     */
    public void contextInterface() {
        strategy.algorithmLogic();
    }
}

代码很简单,增加了factory的方法,这个方法作用就是创建策略对象。这样,客户端就不需要去理解具体的策略,只需知道具体策略的结果就好。看看客户端代码:

public class Client {

    public static void main(String[] args) {
        Context context = new Context();
        context.factory("LOSE");
        context.contextInterface();
    }
}

总结

注意策略模式和工厂方法模式的区别,在前面工厂方法模式中有说到,这里就不再阐述。策略模式本身也相对比较简单,重点在它的扩展以及其它模式的对比,分析各自的优缺点。来看看策略工厂这样的模式存在缺点吗?很明显,如果需要添加或者淘汰一种策略,Context就必须修改,这并不符合开闭原则。在《设计模式之禅》中的提出通过策略枚举和反射机制对策略模式进行改良,膜拜了~但是要添加或淘汰策略,还是得去对枚举进行修改,也不符合开闭原则。根据自己项目情况,选择最适合自己项目的模式。下一篇是责任链模式,欢迎继续关注,goodbye!

设计模式Java源码GitHub下载https://github.com/jetLee92/DesignPattern

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,894评论 1 15
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,716评论 2 17
  • 2017年1月29日 周日 雨 今天,我被一根线给刮到了额头,而且很红,有一点点血液。有一点点痛痛的,只是一点皮外...
    叶赖子阅读 249评论 1 2
  • 今天是星期天,也是世界读书日。带上喜欢的书,约上可爱的人儿,来到了美丽的地方。 第一站:靖港古镇 逛街、赏景…… ...
    碧叶青莲阅读 372评论 0 0