命令模式:将一个请求或者命令封装成一个对象,从而让用户将客户端请求参数化。对请求排队,或者记录日志,以及支持撤销动作。
每次看任何定义都是云里雾里╮(╯▽╰)╭。其实我觉得这个模式就是将要进行的请求封装成对象,为什么要封装成对象?因为这个请求可能不是一个动作能完成的,再者就是这个请求需要依赖与另外的功能类去实现,如果代码直接耦合的持有另一功能类的引用去操作实现功能,会使的代码耦合度比较高,而且不利于修改,以及不能同步去工作,假设现在那个功能类还没写完呢???怎么写持有引用的代码?必然不能啊,这个时候老司机就会想到,接口!下面我们用代码来提出问题并进行演化。
假设有一个包工头类,他要指挥工人去搬砖,铲土,摸水泥等工作,我们先来写一个包工头类。
/**
* 包工头
* @author PC
*
*/
public class Labour {
/**
* 指挥搬砖
*/
public void orderMovingBrick(){
}
/**
* 指挥铲土
*/
public void orderShovel(){
}
/**
* 指挥上水泥
*/
public void orderPutCement(){
}
}
一:如果我们的搬砖工人,铲土工人,上水泥工人还没写出来怎么办?代码如何往下写?
二:假如说有了如何写? 下面是有了的代码:
修改后的代码:
//搬砖工
class BrickPerson{
void mockingBrick(){
};
}
//铲土工
class ShovelPerson{
void shovel(){
}
}
//水泥工
class CementPerson{
void putCement(){
}
}
包工头类:
/**
* 包工头
* @author PC
*
*/
public class Labour {
private BrickPerson brickPerson;
private ShovelPerson shovelPerson;
private CementPerson cementPerson;
public Labour() {
brickPerson = new BrickPerson();
shovelPerson = new ShovelPerson();
cementPerson = new CementPerson();
}
/**
* 指挥搬砖
*/
public void orderMovingBrick(){
brickPerson.mockingBrick();
}
/**
* 指挥铲土
*/
public void orderShovel(){
shovelPerson.shovel();
}
/**
* 指挥上水泥
*/
public void orderPutCement(){
cementPerson.putCement();
}
}
这样没毛病啊,看着很爽啊,哈哈哈。然后过了二个月,又出问题了。
包工头指挥搬砖不需要指挥工人了,现在新出来一批机器人可以搬砖,高效不懂累,不抱怨成本低,哈哈这时候怎么办? 有人说,那就去声明一个机器人引用,然后在orderMovingBrick方法里改一下不就得了? 那好,那我再问,现在需要指挥这个动作同时要指挥20个机器人呢,你怎么办?然后再将代码改成使用List存入20个机器人的引用,然后再movingBrick方法改成循环List调用机器人搬砖的方法?
切记:对修改闭合,对扩展开发,还有我和你讲,你这样改,这堆代码在变过三次需求之后,必须成了烂代码了,干过开发的都知道╮(╯▽╰)╭。
那么到底该肿么办?
思路:将需要执行的命令封装成一个对象。
定义一个高度抽象接口,只有执行的动作,因为命令只需要执行啊!!!:
/**
* 命令接口
* @author PC
*
*/
public interface Command {
/**
* 调取执行的方法
*/
void execute();
}
包工头代码:
/**
* 包工头
* @author PC
*
*/
public class Labour {
private Command movingBrickCommand;
private Command shovelCommand;
private Command putCementCommand;
public Labour(Command movingBrickCommand, Command shovelCommand,
Command putCementCommand) {
super();
this.movingBrickCommand = movingBrickCommand;
this.shovelCommand = shovelCommand;
this.putCementCommand = putCementCommand;
}
/**
* 指挥搬砖
*/
public void orderMovingBrick(){
movingBrickCommand.execute();
}
/**
* 指挥铲土
*/
public void orderShovel(){
shovelCommand.execute();
}
/**
* 指挥上水泥
*/
public void orderPutCement(){
putCementCommand.execute();
}
}
哈哈,这个时候怎么样,我跟你讲,一劳永逸,包工头代码不需要修改了。
看三个命令的实现;
/**
* 搬砖命令
* @author PC
*
*/
class BrickCommand implements Command{
private BrickPerson person;
public BrickCommand(BrickPerson person) {
this.person = person;
}
public void execute() {
person.mockingBrick();
}
}
/**
* 铲土命令
* @author PC
*
*/
class ShovelCommand implements Command{
private ShovelPerson person;
public ShovelCommand(ShovelPerson person) {
this.person = person;
}
public void execute() {
person.shovel();
}
}
/**
* 上水泥命令
* @author PC
*
*/
class PutCementCommand implements Command{
private CementPerson person;
public PutCementCommand(CementPerson person) {
this.person = person;
}
public void execute() {
person.putCement();
}
}
别着急骂,再看调用端:
public class Client {
public static void main(String[] args) {
BrickCommand brickCommand = new BrickCommand(new BrickPerson());
ShovelCommand shovelCommand = new ShovelCommand(new ShovelPerson());
PutCementCommand cementCommand = new PutCementCommand(new CementPerson());
Labour labour = new Labour(brickCommand, shovelCommand, cementCommand);
labour.orderMovingBrick();
labour.orderShovel();
labour.orderPutCement();
}
}
有人会说,这岂不是变复杂了??!!这图什么?代码变得这么多?
这其实是设计模式的大体思想,其实几乎不管什么设计模式,他都要尽力做到这样的效果:
低耦合,对修改闭合,对扩展开放,不要去修改以前的功能实现代码,如果功能需要改变或者升级,最好的效果是仅去修改调用端的代码,而且允许因为功能的改变而产生的代码变多的问题,以及以前功能代码变得无用的问题(可以加过期或者删除),但是不接受去直接修改功能实现代码。
那这个时候去解决我们开始没使用命令模式而产生的问题,假如要换成机器人搬砖呢,并且是20个机器人呢?
定义机器人类;
class Rebot {
void movingBrick(){
System.out.println("机器人在搬砖...");
};
}
定义机器人搬砖命令:
/**
* 机器人搬砖
* @author PC
*
*/
class RebotMovingBrick implements Command{
private List<Rebot> list = new ArrayList<Rebot>();
public RebotMovingBrick(List<Rebot> list) {
super();
list.addAll(list);
}
public void execute() {
for (Rebot rebot : list) {
rebot.movingBrick();
}
}
}
调用段代码:
public class Client {
public static void main(String[] args) {
// BrickCommand brickCommand = new BrickCommand(new BrickPerson());
RebotMovingBrick brickCommand = new RebotMovingBrick(list);
ShovelCommand shovelCommand = new ShovelCommand(new ShovelPerson());
PutCementCommand cementCommand = new PutCementCommand(new CementPerson());
Labour labour = new Labour(brickCommand, shovelCommand, cementCommand);
labour.orderMovingBrick();
labour.orderShovel();
labour.orderPutCement();
}
}
看到没,只需要重新组装一个命令就行了,原本的代码,包工头代码完全没动!以及曾经涉及到的工人代码等等的都完全没有去修改,这是极好的,你新做的功能就只需要关注自己新做的这两个类就行了,然后一组装之后丢该包工头就行了。就算出错了,你也不用去关注其他的代码,就去检查自己的代码就行了(当然,这个包工头架构本身是没问题的)。
总结命令模式;
将命令的调用者与执行者来通过接口隔离。