Java设计模式笔记之命令模式

来源:互联网 发布:mycard点 淘宝 编辑:程序博客网 时间:2024/06/06 02:00

概念

命令模式将“请求”封装成对象,这可以让你使用不同的请求、队列、或者日志请求来参数化其他对象。命令模式也支持撤销操作。

有时候在程序中需要在一个对象中处理很多个请求,这些请求对象没有实现统一的接口,需要写很多的判断语句加以区分不同的请求,然后执行相应的操作。命令模式提供了一种新的思路,通过创建一个新的对象命令对象,定义统一的方法名称,并且命令对象中有一个请求对象属性(组合)封装请求对象,在代码中调用命令对象统一的方法操作请求对象,不用写if语句区分不同的请求对象。

类图

命令模式将请求封装在一个对象中,允许代码像操作对象一样去操作不同的方法,传递并且在合适的时机去调用请求对象的方法。有时候需要执行一个请求,但又不知道具体是什么请求,命令模式可以通过命令对象封装请求,把命令对象当作参数传递,执行命令对象中的方法。这样代码中操作的就是统一的命令对象,就可以把代码规范化。

代码例子

在这我们以《headfirst设计模式》中的例子进行说明。有个遥控器有7个插槽,用来控制不同的电器,每个插槽对应两个按钮,开和关。遥控器有一个总的撤销按钮。如果遥控器的插槽有装置插入,按对应的开、关和撤销按钮就可以控制电器。下面是具体的代码实现。

package cn.lzz.hf.command;/** * 命令对象接口 * @author Administrator * */public interface Command {        /**     * 执行请求     */    void execute();        /**     * 撤销请求     */    void undo();}

package cn.lzz.hf.command;/** * 电灯对象 * @author Administrator * */public class Light {public void on(){System.out.println("Light is on.");}public void off(){System.out.println("Light is off.");}}

package cn.lzz.hf.command;/** * 关灯命令对象 * @author Administrator * */public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light){this.light=light;}@Overridepublic void execute() {// TODO Auto-generated method stublight.off();}@Overridepublic void undo() {// TODO Auto-generated method stublight.on();}}

package cn.lzz.hf.command;/** * 开灯命令对象 * @author Administrator * */public class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light){this.light=light;}@Overridepublic void execute() {// TODO Auto-generated method stublight.on();}@Overridepublic void undo() {// TODO Auto-generated method stublight.off();}}

package cn.lzz.hf.command;/** * 收音机对象 * @author Administrator * */public class Stereo {public void on(){System.out.println("Stereo is on");}public void off(){System.out.println("Stereo is off");}}

package cn.lzz.hf.command;/** * 关收音机命令对象 * @author Administrator * */public class StereoOffCommand implements Command {private Stereo stereo;public StereoOffCommand(Stereo stereo){this.stereo=stereo;}@Overridepublic void execute() {// TODO Auto-generated method stubstereo.off();}@Overridepublic void undo() {// TODO Auto-generated method stubstereo.on();}}

package cn.lzz.hf.command;/** * 开收音机命令对象 * @author Administrator * */public class StereoOnCommand implements Command {private Stereo stereo;public StereoOnCommand(Stereo stereo){this.stereo=stereo;}@Overridepublic void execute() {// TODO Auto-generated method stubstereo.on();}@Overridepublic void undo() {// TODO Auto-generated method stubstereo.off();}}

package cn.lzz.hf.command;/** * 空命令对象 * @author Administrator * */public class NoCommand implements Command {@Overridepublic void execute() {// TODO Auto-generated method stub}@Overridepublic void undo() {// TODO Auto-generated method stub}}

package cn.lzz.hf.command;import java.util.Stack;/** * 遥控器对象 * @author Administrator * */public class RemoteControl {//打开按钮数组private Command[] onCommands;//关闭按钮数组private Command[] offCommands;//撤销命令栈private Stack<Command> undoCommandStack; /** * 初始化 */public RemoteControl(){onCommands=new Command[7];offCommands=new Command[7];Command tempCommand=new NoCommand();for(int i=0;i<7;i++){onCommands[i]=tempCommand;offCommands[i]=tempCommand;}undoCommandStack=new Stack<Command>();}/** * 设置命令对象 * @param soltIndex * @param onCommand * @param offCommand */public void setCommand(int soltIndex,Command onCommand,Command offCommand){onCommands[soltIndex]=onCommand;offCommands[soltIndex]=offCommand;}/** * on * @param soltIndex */public void onButtonWasPressed(int soltIndex){onCommands[soltIndex].execute();//记录命令对象undoCommandStack.push(onCommands[soltIndex]);}/** * off * @param soltIndex */public void offButtonWasPressed(int soltIndex){offCommands[soltIndex].execute();//记录命令对象undoCommandStack.push(offCommands[soltIndex]);}/** * 撤销方法 */public void undoButtonWasPressed(){while(!undoCommandStack.isEmpty()){undoCommandStack.pop().undo();}}@Overridepublic String toString() {StringBuilder stringBuilder=new StringBuilder();stringBuilder.append("\n--------------    Remote Control     ----------------\n");for(int i=0;i<onCommands.length;i++){stringBuilder.append("[solt "+i+"]"+onCommands[i].getClass().getName()+"         "+offCommands[i].getClass().getName()+"\n");}return stringBuilder.toString();}}
package cn.lzz.hf.command;/** * 测试代码 * @author Administrator * */public class RemoteControlTest {public static void main(String[] args) {//遥控器对象RemoteControl control=new RemoteControl();//电灯对象Light light=new Light();Command onLight=new LightOnCommand(light);Command offLight=new LightOffCommand(light);control.setCommand(0, onLight, offLight);//收音机对象Stereo stereo=new Stereo();Command onStereo=new StereoOnCommand(stereo);Command offStereo=new StereoOffCommand(stereo);control.setCommand(1, onStereo, offStereo);System.out.println("开灯-》关灯-》撤销");control.onButtonWasPressed(0);control.offButtonWasPressed(0);control.undoButtonWasPressed();System.out.println("关灯-》开灯-》撤销");control.offButtonWasPressed(0);control.onButtonWasPressed(0);control.undoButtonWasPressed();System.out.println("关闭收音机-》打开收音机-》撤销");control.offButtonWasPressed(1);control.onButtonWasPressed(1);control.undoButtonWasPressed();System.out.println("打开收音机-》关闭收音机-》撤销");control.onButtonWasPressed(1);control.offButtonWasPressed(1);control.undoButtonWasPressed();System.out.println("关灯-》打开收音机-》撤销");control.offButtonWasPressed(0);control.onButtonWasPressed(1);control.undoButtonWasPressed();}}

执行结果

开灯-》关灯-》撤销Light is on.Light is off.Light is on.Light is off.关灯-》开灯-》撤销Light is off.Light is on.Light is off.Light is on.关闭收音机-》打开收音机-》撤销Stereo is offStereo is onStereo is offStereo is on打开收音机-》关闭收音机-》撤销Stereo is onStereo is offStereo is onStereo is off关灯-》打开收音机-》撤销Light is off.Stereo is onStereo is offLight is on.

上面代码中,我们通过命令对象Command封装了Light和Stereo对象的开和关命令,在遥控器中可以调用参数对象Command,利用多态的特性模板化遥控器的代码,兼容不同的电器对象。如果需要增加新的电器,只需要创建相应的Command对象就可以实现,不用更改遥控器中的代码。

遥控器代码初始化时, 7个插槽设置了相同的空命令对象,这样在开、关按钮被按下的时候,不用判断插槽中是否有装置,也就是代码中不用判空。这是开发中常用的一中技巧。

总结

命令模式有以下几个优点     

1、命令模式通过命令对象把操作对象和请求对象解耦;

2、请求对象可以利用多态的特性复用具体的命令对象;

3、一个命令对象可以执行多个请求对象的具体操作;

4、增加一个具体的命令对象很容易。

 

原创粉丝点击