命令模式

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图如下:

  

代码:

复制代码
///   <summary>
///  手机生产命令接口(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)操作。   
•系统需要将一组操作组合在一起,即支持宏命令。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值