命令模式

来源:互联网 发布:ubuntu nvidia smi 编辑:程序博客网 时间:2024/06/03 22:58

命令模式将命令或者请求封装成一个对象,使用时,我们将命令传递给命令的调用者,命令的调用者对于命令如何执行一无所知,只需调用命令的方法来完成命令。命令锁操作的对象(命令接收者)与命令调用者完全分开。其结构图如下:


命令接口定义提供哪些命令,在具体的命令子类中,实现具体的命令操作,具体子类持有命令作用对象,也就是命令接收者的引用。用户类将命令接收者作为构造函数的参数传递给命令对象,创建命令对象这话将命令传递给命令的调用者。

例如,我们有一个按钮类Button,按钮可以支持多种不同的命令,如OnCommand定义打开命令,NextCommand定义下一集命令,MuteCommand定义静音命令,这些命令有调用者Button调用。而命令操作的对象可以是TV或者MP3。这时候可以使用命令模式来解耦TV、MP3和Button之间的关系。这个例子的UML图如下:



首先定义命令接收者(命令操作的对象)设备类Device及其子类,Device类定义设备支持的操作,供命令类调用(而不是供Button调用):

/** * @author Brandon B. Lin *  */public interface Device {public abstract void on();public abstract void next();public abstract void mute();}

/** * @author Brandon B. Lin *  */public class TV implements Device {@Overridepublic void on() {System.out.println("TV On");}@Overridepublic void next() {System.out.println("TV Next");}@Overridepublic void mute() {System.out.println("Mute TV");}}

/** * @author Brandon B. Lin *  */public class MP3 implements Device {@Overridepublic void on() {System.out.println("MP3 On");}@Overridepublic void next() {System.out.println("MP3 Next");}@Overridepublic void mute() {System.out.println("Mute MP3");}}

接着定义命令及其子类,命令作用在Device类上,调用设备的各种操作,命令类将命令及其作用对象绑定:

/** * @author Brandon B. Lin *  */public class OnCommand implements Command {private Device deviceToTurnOn;public OnCommand(Device deviceToTurnOn) {this.deviceToTurnOn = deviceToTurnOn;}@Overridepublic void execute() {deviceToTurnOn.on();}}

/** * @author Brandon B. Lin *  */public class NextCommand implements Command {private Device deviceToNext;public NextCommand(Device deviceToNext) {this.deviceToNext = deviceToNext;}@Overridepublic void execute() {deviceToNext.next();}}

/** * @author Brandon B. Lin *  */public class MuteAllCommand implements Command {private List<Device> devicesToMute;public MuteAllCommand(List<Device> devicesToMute) {this.devicesToMute = devicesToMute;}@Overridepublic void execute() {for (Device device : devicesToMute) {device.mute();}}}

定义完命令类之后,定义命令的调用者Button类,Button类不在乎命令执行什么,作用在谁身上,它只是简单地执行传递给它的命令。

/** * @author Brandon B. Lin *  */public class Button {private Command commandWhenClicked;public Button(String displayName, Command commandWhenClicked) {this.commandWhenClicked = commandWhenClicked;}public void click() {commandWhenClicked.execute();}}

最后来个测试类:

/** * @author Brandon B. Lin *  */public class CommandTest {public static void main(String[] args) {TV tv = new TV();OnCommand onTVCommand = new OnCommand(tv);new Button("OnTV", onTVCommand).click();MP3 mp3 = new MP3();NextCommand nextMP3Command = new NextCommand(mp3);new Button("NextMP3", nextMP3Command).click();List<Device> deviceToMute = new ArrayList<>();deviceToMute.add(tv);deviceToMute.add(mp3);MuteAllCommand muteAllCommand = new MuteAllCommand(deviceToMute);new Button("MuteAllButton", muteAllCommand).click();}}

我们可以看到,Button类在被按下的时候执行什么命令,如何执行,作用于哪些设备身上,都不是Button类关心的事,它只负责调用命令的execute方法执行命令。将这些命令逻辑从Button类中抽离出来,而Button类可以聚焦于Button外观等内容。这样,Button类与Device类实现解耦,如果想改变某个按钮操作的对象,无需对Button类进行更改,只需要传递一个不同的命令即可。同时,定义新的命令变得十分容易。


命令模式是回调函数Callback的面向对象版本,它可以实现撤销(undo)操作。


Java中,线程是命令模式的典型例子。Runnable接口相当于命令模式中的Command,而Thread相当于命令的调用者,只不过这个调用动作是Java自动完成,不需要我们手动编写代码,而在上面的例子中,我们需要调用Button类的click方法。线程不关心Runnable如何执行,操作哪些对象,它只简单执行Runnable中定义的run方法,然后把注意力放在线程本身的管理和控制上。这样一来,Thread和Runnable具体操作的对象实现解耦,很容易定义一个不同的命令(Runnable)而无需更改Thread类。

另外,javax.swing.Action也是命令模式的实现。








0 0
原创粉丝点击