1.概念
將來自客戶端的請(qǐng)求傳入一個(gè)對(duì)象,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化。用于“行為請(qǐng)求者”與“行為實(shí)現(xiàn)者”解耦,可實(shí)現(xiàn)二者之間的松耦合,以便適應(yīng)變化。分離變化與不變的因素。
在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,一個(gè)對(duì)象調(diào)用另一個(gè)對(duì)象,一般情況下的調(diào)用過程是:創(chuàng)建目標(biāo)對(duì)象實(shí)例;設(shè)置調(diào)用參數(shù);調(diào)用目標(biāo)對(duì)象的方法。
但在有些情況下有必要使用一個(gè)專門的類對(duì)這種調(diào)用過程加以封裝,我們把這種專門的類稱作command類。
Command模式可應(yīng)用于
a)整個(gè)調(diào)用過程比較繁雜,或者存在多處這種調(diào)用。這時(shí),使用Command類對(duì)該調(diào)用加以封裝,便于功能的再利用。
b)調(diào)用前后需要對(duì)調(diào)用參數(shù)進(jìn)行某些處理。
c)調(diào)用前后需要進(jìn)行某些額外處理,比如日志,緩存,記錄歷史操作等。
Command模式有如下效果:
a)將調(diào)用操作的對(duì)象和知道如何實(shí)現(xiàn)該操作的對(duì)象解耦。
b)Command是頭等對(duì)象。他們可以像其他對(duì)象一樣被操作和擴(kuò)展。
c)你可將多個(gè)命令裝配成一個(gè)符合命令。
d)增加新的Command很容易,因?yàn)檫@無需改變現(xiàn)有的類。
2.UML

3.代碼
public interface Command {
public void execute();
}
public class ConcreteCommand implements Command {
private Receiver receiver = null;
private String state;
public ConcreteCommand(Receiver receiver){
this.receiver = receiver;
}
public void execute() {
receiver.action();
}
}
public class Receiver {
public void action(){
//真正執(zhí)行命令操作的功能代碼
}
}
public class Invoker {
private Command command = null;
public void setCommand(Command command) {
this.command = command;
}
public void runCommand() {
command.execute();
}
}
public class Client {
public void assemble(){
//創(chuàng)建接收者
Receiver receiver = new Receiver();
//創(chuàng)建命令對(duì)象,設(shè)定它的接收者
Command command = new ConcreteCommand(receiver);
//創(chuàng)建Invoker,把命令對(duì)象設(shè)置進(jìn)去
Invoker invoker = new Invoker();
invoker.setCommand(command);
}
}


下面給個(gè)例子,是模擬對(duì)電視機(jī)的操作有開機(jī)、關(guān)機(jī)、換臺(tái)命令。代碼如下
//命令接收者
public class Tv {
public int currentChannel = 0;
public void turnOn() {
System.out.println("The televisino is on.");
}
public void turnOff() {
System.out.println("The television is off.");
}
public void changeChannel(int channel) {
this.currentChannel = channel;
System.out.println("Now TV channel is " + channel);
}
}
//執(zhí)行命令的接口
public interface Command {
void execute();
}
//開機(jī)命令
public class CommandOn implements Command {
private Tv myTv;
public CommandOn(Tv tv) {
myTv = tv;
}
public void execute() {
myTv.turnOn();
}
}
//關(guān)機(jī)命令
public class CommandOff implements Command {
private Tv myTv;
public CommandOff(Tv tv) {
myTv = tv;
}
public void execute() {
myTv.turnOff();
}
}
//頻道切換命令
public class CommandChange implements Command {
private Tv myTv;
private int channel;
public CommandChange(Tv tv, int channel) {
myTv = tv;
this.channel = channel;
}
public void execute() {
myTv.changeChannel(channel);
}
}
//可以看作是遙控器吧
public class Control {
private Command onCommand, offCommand, changeChannel;
public Control(Command on, Command off, Command channel) {
onCommand = on;
offCommand = off;
changeChannel = channel;
}
public void turnOn() {
onCommand.execute();
}
public void turnOff() {
offCommand.execute();
}
public void changeChannel() {
changeChannel.execute();
}
}
//測(cè)試類
public class Client {
public static void main(String[] args) {
// 命令接收者
Tv myTv = new Tv();
// 開機(jī)命令
CommandOn on = new CommandOn(myTv);
// 關(guān)機(jī)命令
CommandOff off = new CommandOff(myTv);
// 頻道切換命令
CommandChange channel = new CommandChange(myTv, 2);
// 命令控制對(duì)象
Control control = new Control(on, off, channel);
// 開機(jī)
control.turnOn();
// 切換頻道
control.changeChannel();
// 關(guān)機(jī)
control.turnOff();
}
}
執(zhí)行結(jié)果為:
The televisino is on.
Now TV channel is 2
The television is off.


4.應(yīng)用場(chǎng)景
在下面的情況下應(yīng)當(dāng)考慮使用命令模式:
1)使用命令模式作為"CallBack"在面向?qū)ο笙到y(tǒng)中的替代。"CallBack"講的便是先將一個(gè)函數(shù)登記上,然后在以后調(diào)用此函數(shù)。
2)需要在不同的時(shí)間指定請(qǐng)求、將請(qǐng)求排隊(duì)。一個(gè)命令對(duì)象和原先的請(qǐng)求發(fā)出者可以有不同的生命期。換言之,原先的請(qǐng)求發(fā)出者可能已經(jīng)不在了,而命令對(duì)象本身仍然是活動(dòng)的。這時(shí)命令的接收者可以是在本地,也可以在網(wǎng)絡(luò)的另外一個(gè)地址。命令對(duì)象可以在串形化之后傳送到另外一臺(tái)機(jī)器上去。
3)系統(tǒng)需要支持命令的撤消(undo)。命令對(duì)象可以把狀態(tài)存儲(chǔ)起來,等到客戶端需要撤銷命令所產(chǎn)生的效果時(shí),可以調(diào)用undo()方法,把命令所產(chǎn)生的效果撤銷掉。命令對(duì)象還可以提供redo()方法,以供客戶端在需要時(shí),再重新實(shí)施命令效果。
4)如果一個(gè)系統(tǒng)要將系統(tǒng)中所有的數(shù)據(jù)更新到日志里,以便在系統(tǒng)崩潰時(shí),可以根據(jù)日志里讀回所有的數(shù)據(jù)更新命令,重新調(diào)用Execute()方法一條一條執(zhí)行這些命令,從而恢復(fù)系統(tǒng)在崩潰前所做的數(shù)據(jù)更新。