java命令模式实例项目源码介绍。本文大量参考了<<大话设计模式>>一书中命令模式一章.特此声明.诸位看官,请将下面的事例转换成代码.
“我去餐厅吃饭,点了一碗面条,三只鸡翅”
怎么样,够简单的吧,知道你们都比较懒,不想编码,看看下面这个是不是和你想的一样
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 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);
}
}
诸位看官以为如何. 别着急发表意见 想想再说.先看看命令模式的类图.
(上图来自<<大话设计模式>>)
其实在去饭店吃饭的例子里,是可以的,因为不管厨师是做面条还是鸡翅,都可以用一个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,在初学者看来是很模糊的,为什么?这个所以和因为有关系吗?遇到这个问题,只能说内力还不够,继续编码吧.