文档章节

学习设计模式——命令模式

江左煤郎
 江左煤郎
发布于 10/22 00:08
字数 1572
阅读 17
收藏 0

任何模式的出现,都是为了解决一些特定的场景的耦合问题,以达到对修改封闭,对扩展开放的效果。命令模式也不例外:

命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。

解决了这种耦合的好处我认为主要有两点:

1.更方便的对命令进行扩展(注意:这不是主要的优势,后面会提到)

2.对多个命令的统一控制(这种控制包括但不限于:队列、撤销/恢复、记录日志等等)

模式解析:

经典的命令模式包括4个角色:

Command:定义命令的统一接口

CommondImpl:Command接口的实现者,用来执行具体的命令,某些情况下可以直接用来充当Receiver。

Receiver:命令的实际执行者

Invoker:命令的请求者,是命令模式中最重要的角色。这个角色用来对各个命令进行控制。

下面对上面四个角色的经典实现用代码来进行说明,这也是大部分文章对命令模式的运用方式。

经典代码实现:

/*
 * 命令接口,声明执行的操作
 */
public interface Commond {
	/*
	 * 执行命令对应的操作
	 */
	public void excute();
}
/**
 * @ClassName:CommondImpl
 * @Description:命令接口的具体实现类
 */
public class CommondImpl implements Commond{
	/*
	 * 持有相应的接收者对象
	 */
	Receiver receiver;
	//示意属性,命令对象可以有自己的属性
	public int state;
	//构造方法,传入接收者对象
	public CommondImpl(Receiver r){
		receiver=r;
	}
	//调用接收者的对于功能方法,由接收者执行具体的功能
	@Override
	public void excute() {
		//可以做一些其他处理
		receiver.action();
	}
}
/**
 * @ClassName:Receiver
 * @Description:接收者类
 */
public class Receiver {
	/**
	 * @Title:action
	 * @Description:示意操作,真正执行命令相应的操作
	 */
	public void action(){
		//功能代码
	}
}
/**
 * @ClassName:Invoker
 * @Description:在应用程序中通过该类来触发执行命令
 */
public class Invoker {
	private Commond commond;

	public void setCommond(Commond commond) {
		this.commond = commond;
	}
	//调用命令对象执行方法
	public void runCommond(){
		commond.excute();
	}
}
//客户端调用
public class Client {
	public static void main(String[] args) {
		//组装命令对象与接收者对象,将命令对象与接收者对象建立起联系
		Receiver r=new Receiver();
		Commond c=new CommondImpl(r);
		//创建Invoker对象,并设置命令对象,然后运行命令
		Invoker invoker=new Invoker();
		invoker.setCommond(c);
		invoker.runCommond();
	}
}

经典实现解析:

 不知道大家看过上面的代码之后是什么感觉,反正我看过上面的代码之后第一反应确实是越看越糊涂了,主要觉得有几点疑问:

1. 执行命令可以,但是为什么要用命令封装起来,这不是有点脱裤子放屁的感觉么?我完全可以这样写:

public class Client {
	public Client() {
		Receiver receiver = new Receiver();
		receiver.action();
	}
}

这样不是更加简单明了?两个类搞定。

2. 通过继承Command之后,增加命令怎么增加?比如增加一个命令,要改动3个地方:增加一个Command实现,修改Receiver类,修改Client。这好像没有对修改关闭啊?

3. CommondImpl与Receiver类完全耦合了啊,要是有CommondImpl以及另一个不同的CommondImpl要执行的命令在不同的Receiver中怎么办?

那来看看到底疑问在哪里:

1. 确实可以两个类来搞定。但我们要牢记命令模式的初衷:对命令请求者(Invoker)和命令实现者(Receiver)的解耦,方便对命令进行各种控制。打个比方,现在我们要对CommondImpl以及其他一系列命令进行日志记录,并且两个命令之间的操作间隔不能大于1秒。这种情况下要直接用两个类就会有大量的业务逻辑要在客户端进行处理,当命令增加,对每个命令的控制增加时,就会在Client里面产生大量的变化点,这样耦合就出来了,但是采用命令模式之后,对着一系列的命令我们都可以进行控制,这就是对变化点的封装,

2. 增加命令:采用命令模式的时候,我感觉最大的耦合点变化到了Receiver和CommondImpl之间,当然我们可以对Receiver进行抽象,采用接口或者抽象类来封装这个变化,但实际情况中我们会遇到多个命令来至于不同的Receiver,比如A命令来至于ReceiverA,B命令来至于ReceiverB,这种情况下我们怎么应对命令的新增?对这种情况我的理解是命令模式并不能也不需要解决这个问题,因为命令模式的操作单元已经细化到了每一个具体的功能上面,当增加一个具体功能的时候是没有很好的办法对功能实现类进行修改关闭的(当然你可以把每个功能方法放到一个类中,但确实没必要,这个粒度已经很小了),实际上也没有必要的。

适用场景:

1. 命令的发送者和命令执行者有不同的生命周期。命令发送了并不是立即执行。

2. 命令需要进行各种管理逻辑。

3. 需要支持撤消\重做操作(这种状况的代码大家可以上网搜索下,有很多,这里不进行详细解读)。

结论:

 通过对上面的分析我们可以知道如下几点:

1. 命令模式是通过命令发送者和命令执行者的解耦来完成对命令的具体控制的。

2. 命令模式是对功能方法的抽象,并不是对对象的抽象。

3. 命令模式是将功能提升到对象来操作,以便对多个功能进行一系列的处理以及封装。

本文转载自:https://www.cnblogs.com/konck/p/4199907.html

共有 人打赏支持
江左煤郎
粉丝 17
博文 70
码字总数 180198
作品 0
西安
后端工程师
私信 提问
编程中的那些经典套路——设计模式汇总

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

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

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

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

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

gzchen
08/27
0
0
JAVA基础再回首(二十六)——面向对象思想设计原则、设计模式、简单工厂模式、工厂方法模式、单例设计模式之饿汉式和懒汉式、Runtime类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m366917/article/details/52717096 JAVA基础再回首(二十六)——面向对象思想设计原则、设计模式、简单工厂模...

Aduroidpc
2016/10/01
0
0
设计模式知识汇总(附github分享)

写在前面 主要内容 为了更系统的学习设计模式,特地开了这样一个基于Java的设计模式【集中营】,都是笔者在实际工作中用到过或者学习过的一些设计模式的一些提炼或者总检。慢慢地初见规模,也...

landy8530
10/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot源码:启动过程分析(二)

接着上篇继续分析 SpringBoot 的启动过程。 SpringBoot的版本为:2.1.0 release,最新版本。 一.时序图 一样的,我们先把时序图贴上来,方便理解: 二.源码分析 回顾一下,前面我们分析到了下...

Jacktanger
昨天
0
0
Apache防盗链配置,Directory访问控制,FilesMatch进行访问控制

防盗链配置 通过限制referer来实现防盗链的功能 配置前,使用curl -e 指定referer [root@test-a test-webroot]# curl -e "http://www.test.com/1.html" -x127.0.0.1:80 "www.test.com/1.jpg......

野雪球
昨天
2
0
RxJava threading

因为Rx针对异步系统设计,并且Rx也自然支持多线程,所以新的Rx开发人员有时会假设Rx默认是多线程的。在其他任何事情之前,重要的是澄清Rx默认是单线程的。 除非另有说明,否则每次调用onNex...

woshixin
昨天
0
0
Python的安装及文件类型、变量

一、为什么学习python 服务于大数据、人工智能、自动化运维。 简单易学 代码简洁 薪资高 近几年越来越火 二、Python的安装 linux 系统默认安装, CentOS7 默认安装了python2.7 安装ipython y...

枫叶云
昨天
1
0
JeeSite 4.x 树形结构的表设计和用法

有些同仁对于 JeeSite 4 中的树表设计不太了解,本应简单的方法就可实现,却写了很多复杂的语句和代码,所以有了这篇文章。 在 JeeSite 4 中的树表设计我还是相对满意的,这种设计比较容易理...

ThinkGem
昨天
30
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部