Java设计模式:命令模式

来源:互联网 发布:java compareto 排序 编辑:程序博客网 时间:2024/05/16 17:48

1.命令模式

命令模式(Command)属于对象的行为模式。

命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。


2.命令模式的类图


参与的角色:

  • Client:客户角色,创建了一个具体命令(ConcreteCommand)对象并确定其接收者。
  • Command:命令角色:抽象命令接口,定义了命令的行为。
  • ConcreteCommand:具体命令。定义一个接受者和行为之间的弱耦合;实现执行方法execute,负责调用接收者的相应操作。
  • Invoker:请求者角色。负责调用命令对象执行请求,相关方法叫做行动方法action。
  • Receiver:接收者角色。负责具体实施和执行一个请求。

3.示例代码

Client(客户端)
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();    } }

Command(抽象命令)
public interface Command {    void execute();}
ConcreteCommand(具体命令)
public class ConcreteCommand implements Command{    public ConcreteCommand(Receiver receiver)    {        this.receiver = receiver;    }    /**     * 执行方法     */    public void execute()    {        receiver.action();    }    private Receiver receiver;}
Invoker(请求者)
public class Invoker{/** * 构造 * @param command */    public Invoker(Command command)    {        this.command = command;    }    public void action()    {command.execute();    }       private Command command;}
Receiver(接收者),命令的具体执行者
public class Receiver{    public Receiver()    {            }    /**     * 行动方法     */    public void action()    {        System.out.println("Action has been taken.");    }}

4.一个例子:操作录音机


系统需求

有一种录音机,录音机有播音(play)、倒带(rewind)和停止(功能)。用Java语言模拟这个系统。
录音机的键盘是请求者(invoker)角色;使用者是客户;而录音机是接收者角色。提炼一个抽象类Command扮演抽命令,此系统有PlayCommand,StopCommand和RewindCommand3个具体命令。

系统类图

如下:




代码实现

Keypad(请求者)
/** * 接收者角色 */public class Keypad{   private Command playCmd;        private Command rewindCmd;    private Command stopCmd;    /**     * 构造函数,参数是命令     * @param play     * @param stop     * @param rewind     */public Keypad(Command play, Command stop, Command rewind){        // concrete Command registers itself with the invokerplayCmd = play;stopCmd = stop;        rewindCmd = rewind;}//执行方法public void play(){playCmd.execute();}public void stop(){stopCmd.execute();}    public void rewind()    {        rewindCmd.execute();    }}

Command(抽象命令)
/** *  抽象命令 */public interface Command{public abstract void execute ( );}
PlayCommand(具体命令)

public class PlayCommand implements Command{private AudioPlayer myAudio;public PlayCommand ( AudioPlayer a)    {myAudio = a;}public void execute( )    {myAudio.play();}}
StopCommand(具体命令)
public class StopCommand implements Command{       private AudioPlayer myAudio;    public StopCommand ( AudioPlayer a)    {myAudio = a;}public void execute( )    {myAudio.stop();}}

RewindCommand(具体命令)

public class RewindCommand implements Command{private AudioPlayer myAudio;public RewindCommand ( AudioPlayer a)    {    myAudio = a;}public void execute()    {    myAudio.rewind();}}

AudioPlayer(接收者)
public class AudioPlayer{public void play( ){System.out.println("Playing...");}public void rewind( ){System.out.println("Rewinding...");}public void stop(){System.out.println("Stopped.");}                                                                    }

Client(客户端)
/** * 客户端 */public class Client{    /**     *键盘,持有Invoker角色     */    private static Keypad keypad ;    /**     *接受者角色      */    private static AudioPlayer myAudio = new AudioPlayer();public static void main(String[] args){    test1();}    private static void test1()    {    Command play = new PlayCommand(myAudio);        Command stop = new StopCommand(myAudio);        Command rewind = new RewindCommand(myAudio);        keypad = new Keypad(play, stop, rewind);        keypad.play();        keypad.stop();        keypad.rewind();        keypad.stop();        keypad.play();        keypad.stop();    }}  

运行效果:
Playing...
Stopped.
Rewinding...
Stopped.
Playing...
Stopped.

添加宏命令

设想:添加一个宏命令键,把设置好的命令按顺序执行。只需要增加一个宏命令,而不用修改接收者。增加宏命令,使用组合模式。


MacroCommand(宏命令的接口)继承与Command,增加了操作子命令的接口。

public interface MacroCommand extends Command{void execute();void remove(Command toRemove);void add(Command toAdd);}

MacroAudioCommand(宏命令实现)
public class MacroAudioCommand implements MacroCommand{     private List<Command> commandList = new ArrayList<Command>();     public void add(Command toAdd)     {          commandList.add(toAdd);     }     public void remove(int index)     {commandList.remove(index);     }     public void remove(Command toRemove)     {commandList.remove(toRemove);     }     public void execute()      { Command nextCommand;/** * 子命令顺序执行 */for (int i=0; i < commandList.size(); i++){nextCommand = commandList.get(i);nextCommand.execute();        }     } }

Client(客户端)
public class Client {/** * 键盘,持有Invoker角色 */private static Keypad keypad;/** * 接受者角色 */private static AudioPlayer myAudio = new AudioPlayer();public static void main(String[] args) {testMacroCommand();}private static void test1() {Command play = new PlayCommand(myAudio);Command stop = new StopCommand(myAudio);Command rewind = new RewindCommand(myAudio);keypad = new Keypad(play, stop, rewind);keypad.play();keypad.stop();keypad.rewind();keypad.stop();keypad.play();keypad.stop();}private static void testMacroCommand() {Command play = new PlayCommand(myAudio);Command stop = new StopCommand(myAudio);Command rewind = new RewindCommand(myAudio);MacroCommand macro = new MacroAudioCommand();macro.add(play);macro.add(stop);macro.add(rewind);macro.add(play);macro.add(stop);macro.add(rewind);macro.execute();}}

运行效果和test1一致。

5.让命令模式支持undo和redo

要让命令模式支持撤销和恢复功能,首先,抽象命令需要增加接口undo
public interface Command{public abstract void execute ( );public abstract void undo ( );}

然后,具体命令类需要存储状态信息,包括:
(1)接收者对象实际上实施请求所代表的的操作;
(2)对接收者对象所作出的操作所需要的参数;
(3)接收者类的最初的状态。接收者必须提供方法,使命令类可以控制接收者恢复原有状态。

最后,系统需要存储被执行过命令,当undo时,逆序遍历命令,执行undo,当redo时,顺序遍历命令,执行execute。

6.在什么情况下使用命令模式

  • 使用命令模式作为callback在面向对象系统中的替代。callback讲的是:在面向过程编程中先将一个函数登记上,然后在以后调用此函数。
  • 需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求者可以有不同的生命周期。
  • 系统需要支持命令的撤销。
  • 系统需要支持命令的redo,一个系统将所有的数据更新写到日志里,当系统崩溃时,可以读取日志,添加命令,然后redo,恢复数据。

7.命令模式的优点和缺点

优点:
  1. 命令模式把请求一个操作的对象(客户端)与知道怎么执行一个操作的对象(接收者)分开。
  2. 命令类容易修改和扩展。
  3. 容易实现合成命令
缺点:具体命令类可能有很多。






原创粉丝点击