Java 大白话讲解设计模式之 -- 策略模式

声明:原创作品,转载请注明出处https://www.jianshu.com/p/4d53aa2adb22

在开始说设计模式之前,让我们先聊点别的东西,相信每个人都有自己难忘的童年时光,在那时有很多有趣好玩的事物陪伴着我们。我记得小时候,很喜欢看一部动画片--《四驱兄弟》,,,好吧暴露年纪了。。受动画片的影响,还经常和小伙伴们一起买四驱玩具车玩。除了玩具车外,我还喜欢玩游戏机来打发时间,当然不是你想的那种游戏主机什么的,而是这种:


手持游戏机

。。可能现在有些小伙伴不认识这家伙了,这其实就是一个手持的小型游戏机,里面内置了几种游戏,我能够想起的几种游戏一个是比较经典的俄罗斯方块,还有个是很简单的赛车游戏,就是在游戏中不断躲避障碍物,并且速度还会不断加快。种类还是比较少的。好了言归正传,一般工厂生产产品时,都需要一个模具,或者说一套标准,在生产具体的产品时,都得照着这个标准做。好了,那么生产这个游戏机的标准是怎样的呢,我们可以定义一个类来描述这个标准:

/**
 * 工厂中制作游戏机的一个模型标准(类似于模具)
 */
public abstract class GamePlayerModel {
//    每个游戏机都内置一款游戏,具体游戏类型在生产具体的游戏机时装入
    abstract void game();
//    游戏机外观,具体在生产时决定
    abstract void display();
//    点击游戏机上的开始游戏按钮进入游戏
    public void startGame(){
        game();
    }
}


可以看到,在这个游戏机模具中,内置了一款游戏(这里假设每个游戏机中只有一款游戏),当然开始什么也没装,只有在生产具体类型的游戏机时才会装入相应的游戏,还有个开始游戏的方法,当点击开始游戏就会调用内置的游戏。里面还有个display方法,表示游戏机的外观,具体是什么颜色,也只有在生产时才能决定。当然这里的模型我们是经过简化的,事实上要复杂很多。

好了有了这个游戏机模型,我们生产一个游戏机就很方便了,比如我们需要生产一个红色的内置俄罗斯方块游戏的游戏机,我们定义一个类来描述:

public class RedTetrisPlayer extends GamePlayerModel{
    
    @Override
    void game() {
        System.out.print("正在俄罗斯方块游戏中...");
    }

    @Override
    void display() {
        System.out.print("一台红色的游戏机");
    }
}


可以看到,有了之前游戏机模型标准的基础,我们需要一台有俄罗斯方块游戏的游戏机时,在生产时装入这款游戏并给游戏机上了颜色。

接下来我们就试试这款游戏机好不好用:

public class TestClient {

    public static void main(String[] args){
//       一台新鲜出炉的红色的游戏机
        RedTetrisPlayer tetrisGamePlayer = new RedTetrisPlayer();
//        点击游戏机上的开始游戏按钮
        tetrisGamePlayer.startGame();
    }
}


代码执行结果:
---------------------------------------------------------------------------------------------------------------------
正在俄罗斯方块游戏中...
---------------------------------------------------------------------------------------------------------------------

可以看到,俄罗斯方块游戏已经启动,是不是有点激动。。。

从此以后,我便迷上了这个游戏,只要一有空我就会掏出游戏机撸上几局。然而。。。一直玩这个游戏,慢慢我也产生厌倦了,我想换个游戏玩,可是这个游戏机里就一个游戏,怎么办呢。。此时,商店里还有很多装了不同游戏类型的游戏机,我的第一反应就是再去买一个,可是一想,再买一个又得花钱,当时的价格对于我来说简直是天价,毕竟有个5毛一快的,那时候在小伙伴中都可以算个“有钱人”。。。再说即使买了,要是一个两个还好,要是好几个放家里,也是碍事。这时,我突然想到邻居一位大哥哥平时挺喜欢捣鼓这些电子器件的,就想着能不能让他帮我把游戏改改,果然,他说可以,不过操作上还是比较麻烦的需要把机子拆开,然后把游戏代码重新写到设备上,最后还得组装起来。虽然这花了几天时间,但好歹我又有新游戏可以玩了。然而再好玩的游戏也会有玩腻的一天,一段时间后,我又想玩新的游戏了,我第一反应就是去找哪位大哥哥,可是转念一想,改个游戏没想象中的那么简单,每次都需要把机子拆了,重新写程序,再重新组装,想到这些,我又打消了念头。就这样,我把游戏机扔在了一旁,专心看起了我的动画片。直到有一天,我看见小伙伴中有人在玩这个:

正面

我们当时管这叫“gameboy”,也是一种手持游戏机,不过要比之前的那种高端,他的背部有个插槽,可以插入不同的游戏卡。


背面

卧槽!这不正是我需要的吗,想要玩什么游戏,就插入什么卡就行了,简直不要太爽好吧。。

好了,既然出现了个新家伙,我们来看看用代码怎么描述这个它,我们刚才看到比起之前的游戏机它的背部多了个插槽或者叫做接口,用于插入游戏卡。厂家在制作游戏卡时,也会准从一套标准,最主要的标准就是游戏卡上的接口类型要和游戏机上的一致,比如游戏机插槽有几个管脚,那么游戏卡的接口处就得做几个管脚,总不能在这游戏机上插入小霸王的卡把。。除了游戏卡的接口处要和游戏机上的插槽一致外,游戏卡中最重要的就是要有游戏啦,不然要这卡干嘛。这样,只要制作游戏卡的时候都遵循这个标准,那么含有不同游戏的卡就都能插入到游戏机上。好了我们来看下代码:

/**
 * 游戏卡制作的标准,插槽接口处类型为GameBoyKard
 */
public interface GameBoyKard {
//    卡里装的游戏,生产时才装入
    void game();
}

同理,生产GameBoy也有一套标准:

public abstract class GameBoyPlayerModel {

//    游戏机上装有一个类型为GameBoyKard的插槽,表示只允许GameBoy的卡插入
    private GameBoyKard mGameKard;
//    游戏机的外观,有生产过时决定
    public abstract void display();
    
//    用于插入游戏卡
    public void setGameKard(GameBoyKard gameKard){
        this.mGameKard = gameKard;
    }

//    开始游戏按钮
    public void startGame(){
        mGameKard.game();
    }
}

我们可以看到,比起之前的那个游戏机,这个gameboy游戏机已经没有内置的游戏了,而是多了一个插槽,用于插入游戏卡。有了这套标准,我们生产一台gameboy游戏机就方便多了,比如我们也想生产一台红色的:

public class RedGameBoyPlayer extends GameBoyPlayerModel{
    @Override
    public void display() {
        System.out.print("这是一台红色的GameBoy");
    }
}

也是很简单,基于之前定义的标准,我们只要生产时给定颜色就可以了。

这样,我们的游戏机就有了,不过里面可没装任何游戏,我们还需要一张游戏卡:

public class TetrisGameCard implements GameBoyKard {
    @Override
    public void game() {
        System.out.print("正在俄罗斯方块游戏中...");
    }
}

可以看到我们定义了一张装有俄罗斯方块的游戏卡,当然这张卡的设计标准都是遵循GameBoyKard的标准。

好了,游戏机和游戏卡都已经就绪,赶紧来试下吧:

public class TestClient {

    public static void main(String[] args){
//       生产一台红色gameboy
        RedGameBoyPlayer redGameBoyPlayer = new RedGameBoyPlayer();
//        生产一张俄罗斯方块游戏卡
        TetrisGameCard tetrisGameCard = new TetrisGameCard();
//        向游戏机中插入游戏卡
        redGameBoyPlayer.setGameKard(tetrisGameCard);
//        开始游戏
        redGameBoyPlayer.startGame();
    }
}


代码执行结果:
---------------------------------------------------------------------------------------------------------------------
正在俄罗斯方块游戏中...
---------------------------------------------------------------------------------------------------------------------

怎么样,游戏跑起来,还不错把,可能你想玩点其他游戏,没关系,我们只用换张卡就行了,比如有一张赛车的游戏卡:

public class CarGameCard implements GameBoyKard {
    @Override
    public void game() {
        System.out.print("正在赛车游戏中...");
    }
}

只要把这张卡插入游戏机中,就可以更换游戏了:

//        生产一张赛车游戏卡
        CarGameCard carGameCard = new CarGameCard();
//        向游戏机中插入游戏卡
        redGameBoyPlayer.setGameKard(carGameCard);
//        开始游戏
        redGameBoyPlayer.startGame();


代码执行结果:
---------------------------------------------------------------------------------------------------------------------
正在赛车游戏中...
---------------------------------------------------------------------------------------------------------------------

怎么样,是不是感觉gameboy游戏机的这种设计比之前要方便很多。其实这种设计方法正是运用了我们今天的主角--策略模式。好了来看下它的定义:

策略模式 -- 定义算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

定义可能有点抽象,不过想想我们这个游戏机的例子,这里的算法族可以理解为某个游戏,我们用游戏卡封装游戏,这样游戏就从游戏机中独立出来,并且不同的游戏卡可以互相切换,这样你再看下定义是不是就好理解多了呢?

那么这种设计模式对我们代码来讲有哪些优势呢?在上文一开始介绍的游戏机,如果我们想换个游戏玩,有两种方式,第一种,就是再去买个游戏机,但是为了换个游戏,我又多了个游戏机,,我们可以看到游戏机的类都是GamePlayerModel这个抽象类的子类,采用这种方式,必定会造成大量的子类以及重复代码。第二种方式就是直接改写游戏机中的游戏,这种方式也是不推荐的。如果直接在代码中修改,一来比较麻烦,再者,这样频繁修改很容易会影响其他功能模块,对项目的维护性和扩展性大打折扣。相反,我们在看看使用了策略模式的游戏机,我们不再需要定义大量不同的游戏机子类,我们要改变的是游戏,只用创建不同游戏卡类就行了,而且不同的卡之间可以互相切换,对原来的功能模块也不会产生任何影响。

另外在策略模式中我们也可以看出几条设计原则,什么是设计原则呢?我们都知道,每种编程语言都有语法,如果你在编程时不遵守语法,那么编译器就会报错。就好比法律,如果你不守法,那么就会得到制裁。除了守法外,我们都是讲道德的,这是要高于法律的。这里的设计原则就像是我们的品德,当然你可以不遵守这些设计原则,只要编译器不报错就好了,不过我相信大家都是讲道德的人,为了不让我们的代码去坑别人,我们还是来看看设计原则吧。在策略模式中,包含了这几条设计原则:

1.在代码中把那些可能变化的地方分离开来,别让它和不变的代码混在一起。
2.针对接口编程,而不是针对实现编程。
3.多用组合,少用继承。

相信从字面上也是很好理解,我们再结合上面的例子简单讲下。第一条,在最开始的游戏机类中,有两个方法,游戏和外观,一般游戏机生产出来外观就不会有变化了,而我们想改变的是里面的游戏,所以在gameboy游戏机中,游戏从游戏机中分离了出来。第二,在之前的游戏机中,游戏是直接实现在游戏机类中,而gameboy则是用到游戏的接口。第三条,顾名思义,每个gameboy游戏机都能和不同的游戏卡任意组合,当然这个游戏卡,要实现相应的接口。而之前的游戏机类都是继承于父类,里面的游戏也就写死了。

好了,这篇文章到这也就差不多了,相信你已经很好的掌握了策略模式以及其中体现的几点设计原则。如果哪天突然忘了什么是策略模式,就想想小时候玩的游戏机吧!

设计模式持续更新中。。。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,566评论 25 707
  • 今天是进入衔接班的第16天,明天就参加分级分类考试了,心里很着急,可是每天都觉得好忙碌好多事情要做,我的60块钱可...
    琼林玉质阅读 139评论 0 0
  • 关于整理术的书籍和课程满天飞,听到很多人看完书或听完课的总结就是一个字:扔! 肯痛下决心断舍离当然是艰难的第一步,...
    战放如妖阅读 500评论 2 3
  • 我睡着了,妈妈喊了我好几次,都没能让我醒来。 我是真的不愿醒来。如同李白那般“但愿长醉不愿醒”,我是但愿长睡,唯求...
    君子乐阅读 201评论 0 1
  • 其实 有些时候你写了很长的文字 去说你的过去,和你已经释怀的心情 看起来,一切似乎都云淡风轻了 可是, 我想,或许...
    Daosy_氵阅读 172评论 0 0