`
0428loveyu
  • 浏览: 29137 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

命令模式

阅读更多

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


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

例如,我们有一个按钮类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 {

	@Override
	public void on() {
		System.out.println("TV On");
	}

	@Override
	public void next() {
		System.out.println("TV Next");
	}

	@Override
	public void mute() {
		System.out.println("Mute TV");
	}

}

/**
 * @author Brandon B. Lin
 * 
 */
public class MP3 implements Device {

	@Override
	public void on() {
		System.out.println("MP3 On");
	}

	@Override
	public void next() {
		System.out.println("MP3 Next");
	}

	@Override
	public 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;
	}

	@Override
	public void execute() {
		deviceToTurnOn.on();
	}

}

/**
 * @author Brandon B. Lin
 * 
 */
public class NextCommand implements Command {

	private Device deviceToNext;

	public NextCommand(Device deviceToNext) {
		this.deviceToNext = deviceToNext;
	}

	@Override
	public 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;
	}

	@Override
	public 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也是命令模式的实现。








分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics