您的位置:首页 > 编程语言 > Java开发

Java设计模式之命令模式

2016-06-09 12:07 579 查看
当一系列的类能直接找到我,让我帮他们干活时,其实我是拒绝的。我又要接待你们(接收命令),又要干活(执行命令),真是麻烦。自从我有了一个秘书之后,事情就好办多了,因为我再也不需要接待别的类了,因为秘书能帮我接收命令,把方法调用(method invocation)封装起来了,而且我和秘书的耦合程度也很低哦。(解耦)

1. 命令模式

命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

2. 封装方法调用

命令对象不直接实现execute()方法的细节。尽量设计“傻瓜”命令对象,它只懂得调用一个接受者的一个行为,能使调用者和接受者之间的解耦程度比设计聪明的命令对象高。



命令模式结构代码:

1)命令抽象类,用来声明操作的接口(interface会更好)

public abstract class Command {
protected Receiver receiver;

public Command(Receiver receiver){
this.receiver = receiver;
}

abstract public void Execute();

}


2)具体命令类,将一个接受者绑定于一个动作,调用接受者相应的操作,以实现Execute(一系列操作)

class ConcreteCommand extends Command{
public ConcreteCommand(Receiver receiver){
super(receiver);
}

@Override
public void Execute(){
receiver.Action();
}
}


3)传达者,要求该命令执行这个或一系列请求

class Invoker{
private Command command;
//	private List<Command> l = new ArrayList<>();

public void setCommand(Command command){
this.command = command;
}

public void ExecuteCommand(){
command.Execute();
}
}


4)接收者,知道如何实施与执行一个与请求相关的操作

class Receiver{
public void Action(){
/*执行请求*/
System.out.println("执行请求!");
}
}


5)命令接受及执行过程

public class Test {

public static void main(String[] args) {
Receiver r = new Receiver();
Command c = new ConcreteCommand(r);
Invoker i = new Invoker();

i.setCommand(c);
i.ExecuteCommand();

}

}


命令模式的要点:

I. 命令模式将发出请求的对象和执行请求的对象解耦

II. 在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。

III. 调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用(命令可以支持撤销)。

IV. 宏命令是命令的一种延伸,允许调用多个命令。

V. 命令也可以用来实现日志和事务系统。

3. BBQ烧烤档

1)烧烤店厨师:执行烧烤命令的类

/*烧烤者,能执行命令的人*/
class Barbecuer{
public void BakeMutton(){
/*烤羊肉串*/
System.out.println("烤羊肉串");
}

public void BakeChickenWing(){
/*烤鸡翅*/
System.out.println("烤鸡翅");
}

public void BakeBacon(){
/*烤培根*/
System.out.println("烤培根");
}
}


2)抽象命令类,关于烧烤的命令

//抽象命令
interface BBQCommand{
//执行命令
abstract public void excuteCommand();

@Override
public String toString();
}


3)具体命令类,执行命令时,执行具体的行为,烤不同的食材。(如烤鸡翅)

/*具体命令类。执行命令时,执行具体的行为*/
class BakeMuttonCommand implements BBQCommand{   //烤羊肉串命令
Barbecuer barbecuer;

public BakeMuttonCommand(Barbecuer barbecuer) {
this.barbecuer = barbecuer;
}

@Override
public void excuteCommand() {
barbecuer.BakeMutton();
}

@Override
public String toString() {
return this.getClass().getName();
}

}

class BakeChickenWingCommand implements BBQCommand{   //烤鸡翅命令
Barbecuer barbecuer;

public BakeChickenWingCommand(Barbecuer barbecuer) {
this.barbecuer = barbecuer;
}

@Override
public void excuteCommand() {
barbecuer.BakeChickenWing();
}

@Override
public String toString() {
return this.getClass().getName();
}

}

class BakeBaconCommand implements BBQCommand{   //烤培根命令
Barbecuer barbecuer;

public BakeBaconCommand(Barbecuer barbecuer) {
this.barbecuer = barbecuer;
}

@Override
public void excuteCommand() {
barbecuer.BakeBacon();
}

@Override
public String toString() {
return this.getClass().getName();
}

}


4)具体命令类,宏命令。执行此命令时,会执行一系列命令。

//宏命令。(来个烧烤套餐)
class MacroCommand implements BBQCommand{
BBQCommand[] commands;

public MacroCommand(BBQCommand[] commands){
this.commands = commands;
}

@Override
public void excuteCommand() {
for(BBQCommand c:commands){
c.excuteCommand();
}
}

@Override
public String toString() {
return this.getClass().getName();
}

}


5)传递者类,传达请求,与请求者及执行者都解耦了。此处即为服务员。

/*服务员。传递命令的类*/
class Waiter{
private List<BBQCommand> orders = new ArrayList<>();  //存放命令的容器

//设置订单
public void setOrder(BBQCommand command){
if(command.toString() == "Command.BakeChickenWingCommand"){   //货存确认
System.out.println("无存货:" + command.toString());
}else{
orders.add(command);
System.out.println("增加订单:" + command.toString());
}
}

//取消订单
public void cancelOrder(BBQCommand command){
orders.remove(command);
System.out.println("取消订单:" + command);
}

//通知全部执行
public void notifyExecuter(){
for(BBQCommand c:orders){
c.excuteCommand();
}
}
}


6)客户端,即完成命令模式的系统或插件。此处为烧烤店。

public class BBQ {
public static void main(String[] args) {
//开店前的准备
Barbecuer boy = new Barbecuer();
BBQCommand a = new BakeMuttonCommand(boy);
BBQCommand b = new BakeChickenWingCommand(boy);
BBQCommand c = new BakeBaconCommand(boy);

BBQCommand[] SetMeal = {a,b,c};
BBQCommand d = new MacroCommand(SetMeal);  //烧烤套餐

Waiter girl = new Waiter();

//开店营业  顾客点菜
girl.setOrder(a);
girl.setOrder(b);
girl.setOrder(c);
girl.setOrder(d);
girl.setOrder(a);

girl.cancelOrder(a);  //撤销命令
//点菜完毕,通知厨房
girl.notifyExecuter();
}

}


7)运行结果,当命令类名为烤鸡翅时,显示无存货。(实际应用时可在客户端,即烧烤店里记录存货以对比库存)



4. 总结

1)命令模式较为容易地设计一个命令队列;

2)能将命令记入日志,并以此来支持撤销功能

3)加入新的具体命令类不影响其他的类,因此增加新的命令类很容易;

4)封装方法调用中的五点要点。

注意:

敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。(自大话设计模式)

参考资料:

1.《Head First设计模式》

2.《大话设计模式》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: