命令模式:将一个请求或者命令封装成一个对象,从而让用户将客户端请求参数化。对请求排队,或者记录日志,以及支持撤销动作。
每次看任何定义都是云里雾里╮(╯▽╰)╭。其实我觉得这个模式就是将要进行的请求封装成对象,为什么要封装成对象?因为这个请求可能不是一个动作能完成的,再者就是这个请求需要依赖与另外的功能类去实现,如果代码直接耦合的持有另一功能类的引用去操作实现功能,会使的代码耦合度比较高,而且不利于修改,以及不能同步去工作,假设现在那个功能类还没写完呢???怎么写持有引用的代码?必然不能啊,这个时候老司机就会想到,接口!下面我们用代码来提出问题并进行演化。
假设有一个包工头类,他要指挥工人去搬砖,铲土,摸水泥等工作,我们先来写一个包工头类。
/** * 包工头 * @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(); } } 看到没,只需要重新组装一个命令就行了,原本的代码,包工头代码完全没动!以及曾经涉及到的工人代码等等的都完全没有去修改,这是极好的,你新做的功能就只需要关注自己新做的这两个类就行了,然后一组装之后丢该包工头就行了。就算出错了,你也不用去关注其他的代码,就去检查自己的代码就行了(当然,这个包工头架构本身是没问题的)。
总结命令模式;
将命令的调用者与执行者来通过接口隔离。