文档章节

深入浅出设计模式——从球赛中悟命令模式

珂jack
 珂jack
发布于 04/05 18:00
字数 1460
阅读 1449
收藏 47

一、命令模式概念

    命令模式,顾名思义就是向对象发出命令使其执行指定操作。具体的说就是以命令的形式发出请求交给调用对象,随后调用对象又将命令传送给需要处理这条命令的对象并让目标对象执行该命令。

    老规矩,以球赛为例,通常一支球队在比赛中,战术是随着比赛的进行更具场上的情况需要不断的变化。那么教练发出改变战术的指令就是一条命令,战术就是中间的调用对象,而球员则是执行命令的目标对象。我们简单的想象一下,如果没有战术,教练需要对场上的11个人单独指挥,一号你要做什么,二号你号你要做什么……一场比赛下来,教练疯了。有了战术的存在,教练只需要说:马上执行第一套作战方案。然后场上的队员就心领神会,一场比赛下来,轻松惬意。

二、使用场景

    对可变的行为进行处理,实现指令与行为之间的松耦合。

三、结构

    

    从上面的UML图中我们可以看出命令模式由三个部分组成:

        1. 目标对象(Action的实现类):执行命令的对象(球员)。

        2. 调用对象(Command的实现类):命令集(战术)。

        3. 请求者:发出命令的对象。

四、实现

    说了这么多,上干货。

1. 定义目标对象

    球员在场上具有的战术动作其实是相同的,只是不同的时间每个球员需要执行的具体动作是不一样的,所以我需要一个动作包(Action接口),里面包含了所有球员的动作。

public interface Action {
    void attack();  //进攻
    void defend();  //防守
    void relax();   //保持体力
}

    是不是很简单,一共就三个动作。接下来我们开始训练球员,让他们理解这三个动作。

public class Player1 implements Action {
    @Override
    public void attack() {
        System.out.println("1号球员,进攻!");
    }

    @Override
    public void defend() {
        System.out.println("1号球员,防守!");
    }

    @Override
    public void relax() {
        System.out.println("1号球员,保持体力!");
    }
}

    好了,1号球员已经领悟到了动作要领,2,3号球员跟他差不多,我就不一一教他们了。

2. 定义调用对象

    接下来我们要完成调用对象,也就是战术训练。首先需要一个接口,这个接口包含一个执行命令的方法,到时候下令者之要调用这个执行方法,目标对象就会执行相应的方法。

public interface Command {
    void executed();    //执行方法
}

    就这么简单,然后开始布置具体的战术。首先是一号战术:

public class FirstTactics implements Command {

    private Map<Class<? extends Action>, Action> playerMap;

    public FirstTactics(Map<Class<? extends Action>, Action> playerMap) {
        this.playerMap = playerMap;
        System.out.println("开始执行第一套作战方案!");
    }

    @Override
    public void executed() {
        playerMap.forEach((s, o) -> o.attack());
    }

    一号战术接受一个map,这个map就是场上球员的集合,然后executed也就是战术执行方法是让所有球员参与进攻。然后来看看二号战术:

public class SecondTactics implements Command {

    private Map<Class<? extends Action>, Action> playerMap;

    public SecondTactics(Map<Class<? extends Action>, Action> playerMap) {
        this.playerMap = playerMap;
        System.out.println("开始执行第二套作战方案!");
    }


    @Override
    public void executed() {
        Action player1 = playerMap.get(Player1.class);
        Action player2 = playerMap.get(Player2.class);
        Action player3 = playerMap.get(Player2.class);
        player1.attack();
        player2.defend();
        player3.relax();
    }

    二号战术同样接受所有球员的集合,然后它对不同的球员下达了不同的指令。

3. 请求者

    最后我们来看一下命令模式是如何工作的。好了,教练要开始发布命令了。

public class Coach {
    public static void main(String[] args) {
//        Player1 player1 = new Player1();
//        Player2 player2 = new Player2();
//        Player3 player3 = new Player3();
//
//        player1.attack();
//        player2.attack();
//        player3.attack();
//
//        player1.attack();
//        player2.defend();
//        player3.relax();
//
//        player1.attack();
//        player2.attack();
//        player3.attack();

        Map<Class<? extends Action>, Action> playerMap = new HashMap<>();
        playerMap.put(Player1.class, new Player1());
        playerMap.put(Player2.class, new Player2());
        playerMap.put(Player3.class, new Player3());

        new FirstTactics(playerMap).executed();
        new SecondTactics(playerMap).executed();
        new FirstTactics(playerMap).executed();

    }
}

    注释中的内容是在没有使用命令模式的情况下我们要执行同样的操作所需要做的步骤。在只有3个球员的情况下我们已经可以看出它的臃肿,所以直接忽视了。

    我们首先需要把场上所有的队员都加入一个类似于指令库的map里面。然后通过向战术命令中传入球员来得到想要的执行效果。我们来运行一下。

    结果很明显了,只要我们找到对应的战术,球员就会根据战术中布置的指令去执行,这就是命令模式。

五、优点

    通过上面的对比相信大家都能很清楚的看出, 命令模式降低了系统耦合度,并且如果有新的命令(战术)或者战术发生变动,我们也可以很容易去实现。

六、局限性

    如果命令太多系统中可能会存在过多的调用对象,这可能是命令模式的唯一能找出来的不足了。当然了,还是那句话:世界上没有十全十美的模式,每个设计模式都有它适用的地方,只要我们的使用方式得当,那么装饰者模式可以帮助我们写出漂亮优雅的代码。

附源码地址:https://gitee.com/jack90john/command

------------------------------------------------------------------------------

欢迎关注我的个人公众号,推送最新文章

© 著作权归作者所有

共有 人打赏支持
珂jack
粉丝 42
博文 17
码字总数 21342
作品 0
成都
后端工程师
加载中

评论(8)

zcx2001
zcx2001
二号战术中有个小bug:
Action player3 = playerMap.get(Player2.class);
这里应该是Player3.class了吧
orpherus
orpherus
有100个球员就要定义100个PlayerN吗?为什么不是100个带不同参数的实例?
Shazi199
Shazi199
普通开发者:这个地方要用什么设计模式来写?
优秀开发者:使用xx设计模式就能完美解决这个问题
顶级开发者:原来我写的这个是xx设计模式啊
行者__
行者__

引用来自“fengxingzhe”的评论

搜一下,Clojure 实现Java的23种模式

世上本无模式,自扰之
行者__
行者__
搜一下,Clojure 实现Java的23种模式
MrXionGe
MrXionGe
上面的那个 IDEA 关系图真的很好。
很多设计模式,光说,很难理解,一上关系图,思路立马清晰。
就像我们小时候做题一样,很多问题,只要一画图就迎刃而解了……
珂jack
珂jack
谢谢,其实并没有那么厉害。:smile:
Solid
Solid
将设计模式用生活中的例子来描述出来,才是真的掌握了!牛逼!
编程中的那些套路——关于策略模式

该文章属于《编程中的那些经典套路——设计模式汇总》系列,并且以下内容基于语言PHP 今天讲讲策略模式,策略模式 和工厂模式十分相像(或者说在代码逻辑层面,他们是一样的)。 但策略模式与...

gzchen
08/27
0
0
编程中的那些套路——关于工厂模式

该文章属于《编程中的那些经典套路——设计模式汇总》系列,并且以下内容基于语言PHP 前面我们写了简单工厂模式,《编程中的那些套路——关于简单工厂模式》,但简单工厂模式有一些不足(违反...

gzchen
08/27
0
0
编程中的那些套路——关于单例模式

该文章属于《编程中的那些经典套路——设计模式汇总》系列,并且以下内容基于语言PHP 在设计模式中,单例模式和工厂模式)可以说是使用的最普遍的设计模式了,所以掌握此种模式尤为重要。 单...

gzchen
08/27
0
0
编程中的那些经典套路——设计模式汇总

在正式阅读前,我先谈谈我们该用什么姿势和心态学习设计模式: 如果你还没有过多的编程经验(泛指半年以下),我建议你把它当做小说来看,能看懂多少是多少,因为半年以下经验的程序员用到设...

gzchen
08/27
0
0
JavaScript设计模式之观察者模式

前言 准备研究一下MVVM的一些东西,由于MVVM运用了观察者模式的思想,因此翻开了《JavaScript设计模式与开发实践》一书,将观察者模式学习了一遍,顺便有对一些常用的设计模式进行一些了解,...

Srtian
05/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

搬瓦工镜像站bwh1.net被DNS污染,国内打不开搬瓦工官网

今天下午(2018年10月17日),继搬瓦工主域名bandwagonhost.com被污染后,这个国内的镜像地址bwh1.net也被墙了。那么目前应该怎么访问搬瓦工官网呢? 消息来源:搬瓦工优惠网->搬瓦工镜像站b...

flyzy2005
52分钟前
1
0
SpringBoot自动配置

本篇介绍下,如何通过springboot的自动配置,将公司项目内的依赖jar,不需要扫描路径,依赖jar的情况下,就能将jar内配置了@configuration注解的类,创建到IOC里面 介绍下开发环境 JDK版本1.8 spr...

贺小五
今天
3
0
命令行新建Maven多项目

参考地址 # DgroupId 可以理解为包名# DartifactId 可以理解为项目名mvn archetype:generate -DgroupId=cn.modfun -DartifactId=scaffold -DarchetypeArtifactId=maven-archetype-quickst......

阿白
今天
1
0
OSChina 周四乱弹 —— 上帝对我单身年限的惩罚越来越长了

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @达尔文:分享张卫健的单曲《身体健康》 《身体健康》- 张卫健 手机党少年们想听歌,请使劲儿戳(这里) 昨天是重阳节咯, 可惜小小编辑总是晚...

小小编辑
今天
12
1
django rest framework 外键序列化方法与问题总结

django rest framework 外键序列化方法与问题总结 当借口中需要出现一对多关系的时候,我们可以用rest_framwork的序列化功能来处理,代码如下. # models.pyfrom django.db import modelscl...

_Change_
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部