- 命令模式的定义
- 非命令模式实现:
- 命令模式的实现:
说实话这个模式挺令人纠结的,但从这个模式的定义上来看,有点让人摸不到什么头脑,而且查看资料以后会发现还是有点稀里糊涂的,说懂了吧也很简单,也不懂吧也有不懂的理由,于是查阅手头的各种书籍,在此写下心得体会,算是加深一下印象。
命令模式的定义:将请求封装成一个对象,从而让用户使用不同的请求把客户端参数化,以及支持可撤销和恢复的功能。
从定义上来看着实令人一脸懵逼,在这里描述一下博主个人的理解:
请求:客户端要求系统执行的操作,在java的世界里面就是某个对象的方法。
Command:请求封装成的对象,该对象是命令模式的主角。也就是说将请求方法封装成一个命令对象,通过操作命令对象来操作请求方法。在命令模式是有若干个请求的,需要将这些请求封装成一条条命令对象,客户端只需要调用不同的命令就可以达到将请求参数化的目的。将一条条请求封装成一条条命定对象之后,客户端发起的就是一个个命令对象了,而不是原来的请求方法!(行为参数化,在Java8得到了更好的支持)
Receiver:有命令,当然有命令的接收者对象:如果有只有命令,没有接受者,那不就是光棍司令了?没有电视机或者电脑主机,你对着电视机遥控器或者电脑键盘狂按有毛用?Receiver对象的主要作用就是受到命令后执行对应的操作。对于点击遥控器发起的命令来说,电视机就是这个Receiver对象,比如按了待机键,电视机收到命令后就执行了待机操作,进入待机状态。
Client:但是有一个问题摆在眼前,命令对象现在已经有了,但是谁来负责创建命令呢?这里就引出了客户端Client对象,再命令模式中命令是有客户端来创建的。打个比方来说,操作遥控器的那个人,就是扮演的客户端的角色。人按下遥控器的不同按键,来创建一条条命令。
Invoker:现在创建命令的对象Client也已经露脸了,它负责创建一条条命令,那么谁来使用或者调度这个命令呢?--命令的使用者就是Invoker对象了,还是拿人,遥控器,电视机来做比喻,遥控器就是这个Invoker对象,遥控器负责使用客户端创建的命令对象。该Invoker对象负责要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。
上面的讲解着实有些啰嗦,下面就用看电视的人(Watcher),电视机(Television),遥控器(TeleController)来模拟一下这个命令模式,其中Watcher是Client角色,Television是Receiver角色,TeleController是Invoker角色。
首先设计一个简单的电视机的对象:
//电视机对象:提供了播放不同频道的方法
public class Television {
public void playCctv1() {
System.out.println("--CCTV1--");
}
public void playCctv2() {
System.out.println("--CCTV2--");
}
public void playCctv3() {
System.out.println("--CCTV3--");
}
public void playCctv4() {
System.out.println("--CCTV4--");
}
public void playCctv5() {
System.out.println("--CCTV5--");
}
public void playCctv6() {
System.out.println("--CCTV6--");
}
}
电视机的类创建好了,本文会以“非命令模式“和“命令模式“两种实现看电视的不同之处,加深对命令模式的理解。
非命令模式实现:如果不用命令模式的话,其实实现看电视的功能很简单,首先设计一个看电视的人的类:Watcher;既然要看电视,所以Watcher内部需要持有一个电视机的引用。如此简单的Watcher搞定:
//观看电视的死宅类
public class Watcher {
//持有一个
public Television tv;
public Watcher(Television tv) {
this.tv = tv;
}
public void playCctv1() {
tv.playCctv1();
}
public void playCctv2() {
tv.playCctv2();
}
public void playCctv3() {
tv.playCctv3();
}
public void playCctv4() {
tv.playCctv4();
}
public void playCctv5() {
tv.playCctv5();
}
public void playCctv6() {
tv.playCctv6();
}
}
所以简单的调用就实现了:
public static void main(String args[]) {
Watcher watcher = new Watcher(new Television());
watcher.playCctv1();
watcher.playCctv2();
watcher.playCctv3();
watcher.playCctv4();
watcher.playCctv5();
watcher.playCctv6();
}
执行结果:
--CCTV1--
--CCTV2--
--CCTV3--
--CCTV4--
--CCTV5--
--CCTV6--
可以看出Watcher类和Television完全的耦合在一起了,目前本文的电视机对象只能播放六个电视台,如果需要增添全国所有主流卫视的话,需要做如下改动: 1、修改Television对象,增加若干个的playXXTV()的方法来播放不同的卫视。 2、修改Watcher,也添加若干个对应的playXXTV()的方法,调用Television的playXXTV(),如下:
public void playXXTV() {
tv.playXXTV();
}
但是这明显违背了“对修改关闭,对扩展开放“的重要设计原则。 别的不说,就拿本看电视来说,比如调用playXXTV()的顺序是随即的,也就是你胡乱切换了一通:比如你沿着cctv1、cctv2、cctv3、cctv4、xxtv、yytv…nntv的顺序来看电视,也就是发生了如下调用:
watcher.playCctv1();
watcher.playCctv2();
watcher.playCctv3();
watcher.playCctv4();
watcher.playCctv5();
watcher.playCctv6();
watcher.playXXtv();
watcher.playYYtv();
watcher.playNNtv();
,当前你在看nntv,如果你想看上一个看过的台也就是yytv,这很简单,只要在playNNtv() 后面,调用watcher.playYYtv();即可,但是如果你想要一直回退到cctv1呢?那么你就话发生如下可怕的调用:
watcher.playCctv1();
watcher.playCctv2();
watcher.playCctv3();
watcher.playCctv4();
watcher.playCctv5();
watcher.playCctv6();
watcher.playXXtv();
watcher.playYYtv();
watcher.playNNtv();
watcher.playYYtv();
watcher.playXXtv();
watcher.playCctv6();
watcher.playCctv5();
watcher.playCctv4();
watcher.playCctv3();
watcher.playCctv2();
watcher.playCctv1();
为什么会这样呢?因为对于之前播放的哪个卫视并没有记录功能。是时候让命令模式来出来解决 问题了,通过命令模式的实现,对比下就能体会到命令模式的巧妙之处。
命令模式的实现:1、设计一个抽象的命令类: 在本系统中,命令的接收者对象就是电视机Tevevision了:
public abstract class Command {
//命令接收者:电视机
protected Television television;
public Command(Television television) {
this.television = television;
}
//命令执行
abstract void execute();
}
将播放各个卫视的操作封装成一个一个命令,实现如下:
//播放cctv1的命令
public class CCTV1Command extends Command {
@Override
void execute() {
television.playCctv1();
}
}
//播放cctv2的命令
public class CCTV6Command extends Command {
@Override
void execute() {
television.playCctv2();
}
}
。。。。。。。。
//播放cctv6的命令
public class CCTV1Command extends Command {
@Override
void execute() {
television.playCctv6();
}
}
抽象类Command的几个子类实现也很简单,就是将电视机TeleVision对象的playXxTV方法分布于不同的命令对象中,且因为不同的命令对象拥有共同的抽象类,我们很容易将这些名利功能放入一个数据结构(比如数组)中来存储执行过的命令。
命令对象设计好了,那么就引入命令的调用着Invoker对象了,在此例子中电视遥控器TeleController就是扮演的这个角色:
public class TeleController {
//播放记录
List historyCommand = new ArrayList();
//切换卫视
public void switchCommand(Command command) {
historyCommand.add(command);
command.execute();
}
//遥控器返回命令
public void back() {
if (historyCommand.isEmpty()) {
return;
}
int size = historyCommand.size();
int preIndex = size-2
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?