http://www.cnblogs.com/java-my-life/archive/2012/06/01/2526972.html
http://www.cnblogs.com/ejiyuan/archive/2012/06/28/2567905.html
在阎宏博士的《JAVA与模式》一书中开头是这样描述命令(Command)模式的:
命令模式属于对象的行为模式。命令模式又称为行动(Action)模式或交易(Transaction)模式。
命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
命令模式的结构
命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
命令允许请求的一方和接收请求的一方能够独立演化,从而具有以下的优点:
(1)命令模式使新的命令很容易地被加入到系统里。
(2)允许接收请求的一方决定是否要否决请求。
(3)能较容易地设计一个命令队列。
(4)可以容易地实现对请求的撤销和恢复。
(5)在需要的情况下,可以较容易地将命令记入日志。
下面以一个示意性的系统,说明命令模式的结构。
命令模式涉及到五个角色,它们分别是:
● 客户端(Client)角色:创建一个具体命令(ConcreteCommand)对象并确定其接收者。
● 命令(Command)角色:声明了一个给所有具体命令类的抽象接口。
● 具体命令(ConcreteCommand)角色:定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。
● 请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。
● 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
invoker一般只有两个接口,一个是setcommand, 一个是action. 首先一个任务的具体执行者称之为receiver, 将一个receiver对象传参给command对象,可以理解为是command包装了receiver, 然后,再将command通过setcommand传参给invovker,invovker不论是什么命令,就只调用command的execute函数,而execute里调用的是receiver的具体的行为。
源代码
接收者角色类
public class Receiver { /** * 真正执行命令相应的操作 */ public void action(){ System.out.println("执行操作"); } }
抽象命令角色类
public interface Command { /** * 执行方法 */ void execute(); }
具体命令角色类
public class ConcreteCommand implements Command { //持有相应的接收者对象 private Receiver receiver = null; /** * 构造方法 */ public ConcreteCommand(Receiver receiver){ this.receiver = receiver; } @Override public void execute() { //通常会转调接收者对象的相应方法,让接收者来真正执行功能 receiver.action(); } }
请求者角色类
public class Invoker { /** * 持有命令对象 */ private Command command = null; /** * 构造方法 */ public Invoker(Command command){ this.command = command; } /** * 行动方法 */ public void action(){ command.execute(); } }
客户端角色类
public class Client { public static void main(String[] args) { //创建接收者 Receiver receiver = new Receiver(); //创建命令对象,设定它的接收者 Command command = new ConcreteCommand(receiver); //创建请求者,把命令对象设置进去 Invoker invoker = new Invoker(command); //执行方法 invoker.action(); } }
AudioPlayer系统
小女孩茱丽(Julia)有一个盒式录音机,此录音机有播音(Play)、倒带(Rewind)和停止(Stop)功能,录音机的键盘便是请求者(Invoker)角色;茱丽(Julia)是客户端角色,而录音机便是接收者角色。Command类扮演抽象命令角色,而PlayCommand、StopCommand和RewindCommand便是具体命令类。茱丽(Julia)不需要知道播音(play)、倒带(rewind)和停止(stop)功能是怎么具体执行的,这些命令执行的细节全都由键盘(Keypad)具体实施。茱丽(Julia)只需要在键盘上按下相应的键便可以了。
录音机是典型的命令模式。录音机按键把客户端与录音机的操作细节分割开来。
源代码
接收者角色,由录音机类扮演
public class AudioPlayer { public void play(){ System.out.println("播放..."); } public void rewind(){ System.out.println("倒带..."); } public void stop(){ System.out.println("停止..."); } }
抽象命令角色类
public interface Command { /** * 执行方法 */ public void execute(); }
具体命令角色类
public class PlayCommand implements Command { private AudioPlayer myAudio; public PlayCommand(AudioPlayer audioPlayer){ myAudio = audioPlayer; } /** * 执行方法 */ @Override public void execute() { myAudio.play(); } }
public class RewindCommand implements Command { private AudioPlayer myAudio; public RewindCommand(AudioPlayer audioPlayer){ myAudio = audioPlayer; } @Override public void execute() { myAudio.rewind(); } }
public class StopCommand implements Command { private AudioPlayer myAudio; public StopCommand(AudioPlayer audioPlayer){ myAudio = audioPlayer; } @Override public void execute() { myAudio.stop(); } }
请求者角色,由键盘类扮演
public class Keypad { private Command playCommand; private Command rewindCommand; private Command stopCommand; public void setPlayCommand(Command playCommand) { this.playCommand = playCommand; } public void setRewindCommand(Command rewindCommand) { this.rewindCommand = rewindCommand; } public void setStopCommand(Command stopCommand) { this.stopCommand = stopCommand; } /** * 执行播放方法 */ public void play(){ playCommand.execute(); } /** * 执行倒带方法 */ public void rewind(){ rewindCommand.execute(); } /** * 执行播放方法 */ public void stop(){ stopCommand.execute(); } }
客户端角色,由茱丽小女孩扮演
public class Julia { public static void main(String[]args){ //创建接收者对象 AudioPlayer audioPlayer = new AudioPlayer(); //创建命令对象 Command playCommand = new PlayCommand(audioPlayer); Command rewindCommand = new RewindCommand(audioPlayer); Command stopCommand = new StopCommand(audioPlayer); //创建请求者对象 Keypad keypad = new Keypad(); keypad.setPlayCommand(playCommand); keypad.setRewindCommand(rewindCommand); keypad.setStopCommand(stopCommand); //测试 keypad.play(); keypad.rewind(); keypad.stop(); keypad.play(); keypad.stop(); } }
运行结果如下:
///
问题:
在面向对象的软件设计中,经常会遇到一个(或一系列)对象,对象本身的数据存储与对象的操作耦合在一起。例如一个对象有add(),edit(),delete()方法,这样对象支持的方法很难扩展,如果需要加入update()就必须修改代码,客户端与对象也是紧耦合的。命令模式是将一类对象的功能(行为,功能)抽象成一个命令对象,客户端在使用的时候,只与该命令对象打交道,而不用与对象打交道,分离命令的请求者和命令的执行者,降低了耦合性,可以使用不同的请求对客户进行参数化提高了程序设计的灵活性。
定义:
命令模式(Command)模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
意图:
提供一个抽象的Command接口,将执行命令操作的方法封装到Command类接口中,ConcreteCommand实现这个Command接口方法,通过调用Receiver实例变量处理请求。客户端定义一个Invoker对象存储该concreteCommand对象,该invoker通过调用command对象的递交一个请求。
参与者:
•抽象命令角色(Command):
定义命令的接口,声明执行的方法。
具体命令角色(ConcreteCommand):
命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
•请求者(Invoker):
要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
•接收者(Receiver、执行者): (接受者内部有执行各种命令的方法,而一个具体的命令类就调用接收者内部的某个类,而对于这个请求者而言,只需要创建自己需要的某个具体类就行了)
接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
•客户端(Client):
创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。
UML图:
实例说明:
诺基亚手机工厂
公司(Client)通知生产部(Invoker),生产两部n8,两部n9,生产部通过“命令模式”,将生产任务交给手机工厂(Receiver),生产手机。
uml图如下:
代码:
/// 手机生产命令接口(Command)
/// </summary>
public interface ICreatePhoneCommand
{
void Execute();
}
/// <summary>
/// N8手机生产具体命令类(ConcreteCommand)
/// </summary>
public class CreateNokiaN8Command : ICreatePhoneCommand
{
PhoneFactory phoneFactory = null;
public CreateNokiaN8Command(PhoneFactory _phoneFactory)
{
phoneFactory = _phoneFactory;
}
public void Execute()
{
phoneFactory.CreateNokiaN8();
}
}
/// <summary>
/// N8手机生产具体命令类(ConcreteCommand)
/// </summary>
public class CreateNokiaN9Command : ICreatePhoneCommand
{
PhoneFactory phoneFactory = null;
public CreateNokiaN9Command(PhoneFactory _phoneFactory)
{
phoneFactory = _phoneFactory;
}
public void Execute()
{
phoneFactory.CreateNokiaN9();
}
}
/// <summary>
/// 手机生产工厂(Receiver)具体的手机生产
/// </summary>
public class PhoneFactory
{
public void CreateNokiaN8()
{
System.Console.WriteLine( " 一部Nokia N8 生产完成 ");
}
public void CreateNokiaN9()
{
System.Console.WriteLine( " 一部Nokia N9 生产完成 ");
}
}
/// <summary>
/// 生产部对象(Invoker)接收生产信息,制定生产清单。通知PhoneFactory生产
/// </summary>
public class LiaisonCreate
{
List<ICreatePhoneCommand> createPhoneCommandList = new List<ICreatePhoneCommand>();
/// <summary>
/// 添加生产任务
/// </summary>
/// <param name="_createPhoneCommand"></param>
public void AddCreatePhoneTask(ICreatePhoneCommand _createPhoneCommand)
{
createPhoneCommandList.Add(_createPhoneCommand);
}
/// <summary>
/// 撤销生产任务
/// </summary>
/// <param name="_createPhoneCommand"></param>
public void CancelCreatePhoneTask(ICreatePhoneCommand _createPhoneCommand)
{
createPhoneCommandList.Remove(_createPhoneCommand);
}
/// <summary>
/// 执行生产
/// </summary>
public void CreatePhone()
{
foreach ( var createPhoneCommand in createPhoneCommandList)
{
createPhoneCommand.Execute();
}
}
}
public void CommandTest()
{
// 初始化生产部联系人
LiaisonCreate liaisonCreate = new LiaisonCreate();
// 初始化生产工厂
PhoneFactory phoneFactory = new PhoneFactory();
// 设置生产清单
liaisonCreate.AddCreatePhoneTask( new CreateNokiaN8Command(phoneFactory));
liaisonCreate.AddCreatePhoneTask( new CreateNokiaN8Command(phoneFactory));
liaisonCreate.AddCreatePhoneTask( new CreateNokiaN9Command(phoneFactory));
liaisonCreate.AddCreatePhoneTask( new CreateNokiaN9Command(phoneFactory));
// 取消一部N9的生产
liaisonCreate.CancelCreatePhoneTask( new CreateNokiaN9Command(phoneFactory));
// 开始执行生产
liaisonCreate.CreatePhone();
System.Console.Read();
}
优点:
•命令模式将发出命令的责任和执行命令的责任分割开,降低系统的耦合度。
•新的命令可以很容易地加入到系统中。只要实现了抽象命令接口的具体命令类就可以与接收者相关联。
•可以比较容易地设计一个组合命令,形成一个轻量级的事件队列
•命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
•请求方不必知道接收请求的接口,执行命令的细节(只需客户端为concreteCommand对象指定一个receiver对象即可)起到了很好的封装隔离作用.
缺点:
•每一个命令都需要设计一个具体命令类,使用命令模式会导致某些系统有过多的具体命令类。
应用情景:
•系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
•系统需要在不同的时间指定请求、将请求排队和执行请求。
•系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
•系统需要将一组操作组合在一起,即支持宏命令。