从饭店聊命令模式

    本文虽为原创,但大量参考了<<大话设计模式>>一书中命令模式一章.特此声明.

    诸位看官,请将下面的事例转换成代码.
    "我去餐厅吃饭,点了一碗面条,三只鸡翅"
    怎么样,够简单的吧,知道你们都比较懒,不想编码,看看下面这个是不是和你想的一样
package com.command;
    public class NoodlesReceiver {

    String name;
    public NoodlesReceiver(String name){
        this.name=name;
    }

    @Override
    public void doSomeThing(int table_num,int food_count) {
        // TODO Auto-generated method stub
        System.out.println(name+"给"+table_num+"号桌下"+food_count+"碗面条");
    }
}

package com.command;
public class BakeChickenWingReceiver {

    String name;
    public BakeChickenWingReceiver(String name){
           this.name=name;
    }
    
    
    public void doSomeThing(int table_num,int food_count) {
        // TODO Auto-generated method stub
        System.out.println(name+"给"+table_num+"号桌来"+food_count+"个烤鸡翅");
    }
}

package com.command;
public class client {
    public static void main(String[] args) {
        //初始化两个厨师  
        NoodlesReceiver noodlesReceiver=
                new NoodlesReceiver("下面条的李师傅");
        BakeChickenWingReceiver bakeChickenWingReceiver=
                new BakeChickenWingReceiver("烤鸡翅的张师傅");
        
        noodlesReceiver.doSomeThing(10, 1);
        bakeChickenWingReceiver.doSomeThing(10, 3);
    }

}


结果如下
下面条的李师傅给10号桌下1碗面条
烤鸡翅的张师傅给10号桌来3个烤鸡翅

怎么样,和你想的一样吗?
可是看上面的例子,如果再返回现实,那就是你去了饭店,直接对烤肉师傅和面条师傅下命令!开玩笑,咱都是有身份证的人,能亲自跑到满是油烟的厨房去?咋能没有服务员呢?(况且从软件设计的角度来讲,命令的发出者与实行者最好解耦).

第一次重构

两个厨师不变.加一个服务员.
package com.command;
public class Waiter2 {
    NoodlesReceiver noodlesReceiver;
    BakeChickenWingReceiver bakeChickenWingReceiver;
    String name;
    public Waiter2(String name){
        this.name=name;
        //初始化两个厨师  一个服务员
         noodlesReceiver=new NoodlesReceiver("下面条的李师傅");
         bakeChickenWingReceiver=new BakeChickenWingReceiver("烤鸡翅的张师傅");
    }
    
    public void order(int table_num,int food_count,String food){
    
        switch (food) {
        case "鸡翅":
            bakeChickenWingReceiver.doSomeThing(table_num, food_count);
            break;
        case "面条":
            noodlesReceiver.doSomeThing(table_num, food_count);
            break;
        default:
            System.out.println("sorry 小店没有您要点的食物");
            break;
        }
    }
}
package com.command;
public class client2 {
    public static void main(String[] args) {
        
        Waiter2 waiter2=new Waiter2("小美");
        waiter2.order(10, 1, "面条");
        waiter2.order(10, 3, "鸡翅");
        waiter2.order(10, 3, "馒头");
    }
}



结果如下:
下面条的李师傅给10号桌下1碗面条
烤鸡翅的张师傅给10号桌来3个烤鸡翅
sorry 小店没有您要点的食物

看看client2 似乎还蛮不错的,给服务员说几号桌子点什么东西点几份,都很好呀,很符合实际.嗯,确实和很符合实际.问题是
1:大家看看服务员的类.服务员类里面包含两个厨师类,咱们暂且不论如果以后厨师发生变动,还需要改服务员的类,就是现实世界中也不不是这样呀!现实世界是:服务员是服务员,厨师是厨师,这俩没关系!
2:你去饭店吃饭,你是点完一个菜,就让服务员通知一次厨师?你要敢这样,服务员肯定心里骂你:nnd,你丫能不能一次说完,让我少跑几趟.
3:我刚点完1碗面条就不能更改了,我后悔了不想吃了,都不行.这也不符合实际.只有你的面条还没下锅,我就能取消.


我得告诉大家这次重构很遗憾,失败了!那咋办?回想实际情况.
实际情况并不是服务员给厨师下命令,也不是你点一道菜服务员就跑一次.而是服务员带着要给小本,上面可以写一条条的命令.等你点了十道八道菜后,有后悔了,推掉两道菜后,说ok了,服务员才将命令整体发送.对吧,实际难道不是这样?
命令,命令!!! 在这个问题里,命令是根本.各位看官请注意,不要钻牛角尖,大家可以这样想,这里的命令不是动词,不是人的一个方法,而是名词,是你写出来的一个"实体".且命令中包含接受命令的人.简单的说,命令本身知道,自己要传递给谁.(万物皆对象,且都有自己的属性,方法)

第二次重构

2.1次重构.

既然现在有两种厨师,那就先设置两种命令.而且要知道自己是传递给谁的,为了统一,给那两个厨师都加要给父类(接口).
public interface IReceiver {
    public void doSomeThing(int table_num,int food_count);
}


让两个厨师都实现IReceiver.代码不再赘述.

同理我们得让两个命令也都有个父类(接口).
package com.command;
public interface ICommand {
    public void action();
}

package com.command;
public class BakeChickenWingCommand implements ICommand{

    int  count;
    int  num;
    IReceiver receiver;
    public BakeChickenWingCommand(IReceiver receive,int count,int num){
        this.receiver=receive;
        this.count=count;
        this.num=num;
    }
    @Override
    public void action() {
        // TODO Auto-generated method stub
        receiver.doSomeThing(num, count);    
    }
}


面条的命令不再赘述,和鸡翅基本一样,不对,我看看,我擦 完全一样!
咋办?既然都一样,按就不分父类子类了,直接就是下面的样子

2.2次重构


public class Command {

    int  count;
    int  num;
    IReceiver receiver;
    public Command(IReceiver receive,int count,int num){
        this.receiver=receive;
        this.count=count;
        this.num=num;
    }
    
    public void action() {
        // TODO Auto-generated method stub
        receiver.doSomeThing(num, count);    
    }
}


诸位看官以为如何. 别着急发表意见 想想再说.先看看命令模式的类图.


                                                                                                                                                                                                 (上图来自<<大话设计模式>>)

其实在去饭店吃饭的例子里,是可以的,因为不管厨师是做面条还是鸡翅,都可以用一个doSomeThing来代替.可换一个场景呢?古代皇帝,让臣下打仗,镇守地方,升官,贬职你还都用一个doSomeThing搞定吗?所以必须分层必须用接口.
所以很遗憾,2.2次重构又失败了.但是就像哪个谁谁谁说的" 没有经历过苦痛的顿悟是轻佻的",我们只有错过很多次才能真正的"对".

2.3次重构

越过上面的岔路,下面就是服务员了,刚才已经说了,服务员至少得提升两点,一能存储命令,打包发送,二就是能在提交所以命令前,撤销一部分命令,代码如下
package com.command;
import java.util.ArrayList;
public class Waiter {
    String name;
    ArrayList<ICommand> list;
    
    public Waiter(String name){
        this.name=name;
    }
    
    public void addCommand(ICommand command){
        if(list==null)
            list=new ArrayList<ICommand>();
        list.add(command);        
    }
    
    public void removeCommand(ICommand command){
        System.out.println("顾客取消订单");
        list.remove(command);        
    }
    
    public void submit(){
        int n=list.size();
        for(int i=0;i<n;i++)
            list.get(i).action();
    }
}


再来看看客户端的调用

package com.command;

public class Client {
    public static void main(String[] args) {
        //初始化两个厨师  一个服务员(可以理解为饭店聘请了这三个人)
        NoodlesReceiver noodlesReceiver=
                new NoodlesReceiver("下面条的李师傅");
        BakeChickenWingReceiver bakeChickenWingReceiver=
                new BakeChickenWingReceiver("烤鸡翅的张师傅");
        Waiter waiter=new Waiter("小美");
        
        //顾客发出三个命令
        NoodelsCommand noodelsCommand1=
                new NoodelsCommand(noodlesReceiver, 6, 3);
        NoodelsCommand noodelsCommand2=
                new NoodelsCommand(noodlesReceiver, 30, 2);        
        BakeChickenWingCommand bakeChickenWingCommand=
                new BakeChickenWingCommand(bakeChickenWingReceiver, 4, 8);
        
        //服务员记录三个命令后 又撤销一个
        waiter.addCommand(noodelsCommand1);
        waiter.addCommand(noodelsCommand2);
        waiter.addCommand(bakeChickenWingCommand);
        waiter.removeCommand(noodelsCommand2);
        
        //服务员将菜单上交  厨师做菜
        waiter.submit();                
    }
}


结果如下

顾客取消订单
下面条的李师傅给3号桌下6碗面条
烤鸡翅的张师傅给8号桌来4个烤鸡翅


好了大功告成
现在总结一下命令模式的好处
1:它实现了命令队列;
2:把命令看成一个个实体,使得每做一次命令都能记下日志;
3:命令可撤销 这一点在某些有这样需求的系统中很有用;
4:也是最重要的一点 它让一个任务的请求者和实现者分离;
5:新加入的命令只要实现ICommand接口就好,方便扩展.


另外,本人的一点心得,设计模式有很多问题,例如因为xxx所以xxx,在初学者看来是很模糊的,为什么?这个所以和因为有关系吗?遇到这个问题,只能说内力还不够,继续编码吧.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值