注:示例来自《大话设计模式》
假如现有如下场景 路边有个烤肉摊 可以点烤肉吃 简单代码实现如下
烤肉串者类
package Test23;
//烤肉串者
public class Barbecuer {
//烤羊肉
public void BakeMutton()
{
System.out.println("烤羊肉串!");
}
//烤鸡翅
public void BakeChickenWing()
{
System.out.println("烤鸡翅!");
}
}
客户端代码
package Test23;
public class Program {
public static void main(String[] args) {
Barbecuer boy = new Barbecuer();
boy.BakeMutton();
boy.BakeMutton();
boy.BakeMutton();
boy.BakeChickenWing();
boy.BakeMutton();
boy.BakeMutton();
boy.BakeChickenWing();
}
}
上面的写法 如果吃的人多了 就容易乱了 比如谁先来的 谁后来的 谁给没给钱 一个人也不好记
下面我们用烤肉店的方式进行实现 烤肉店里有服务员 我们吃烤肉告诉服务员 然后服务员发命令给烤肉师傅 简单代码实现如下
抽象命令类
package Test23;
//抽象命令
public abstract class Command {
protected Barbecuer receiver;
public Command(Barbecuer receiver)
{
this.receiver = receiver;
}
//执行命令
abstract public void ExcuteCommand();
}
具体命令类
package Test23;
//烤羊肉串命令
public class BakeMuttonCommand extends Command {
public BakeMuttonCommand(Barbecuer receiver) {
super(receiver);
}
@Override
public void ExcuteCommand() {
receiver.BakeMutton();
}
}
package Test23;
//烤鸡翅命令
public class BakeChickenWingCommand extends Command {
public BakeChickenWingCommand(Barbecuer receiver) {
super(receiver);
}
@Override
public void ExcuteCommand() {
receiver.BakeChickenWing();
}
}
烤肉串者类
package Test23;
//烤肉串者
public class Barbecuer {
//烤羊肉
public void BakeMutton()
{
System.out.println("烤羊肉串!");
}
//烤鸡翅
public void BakeChickenWing()
{
System.out.println("烤鸡翅!");
}
}
服务员类
package Test23;
//服务员
public class Waiter {
private Command command;
//设置订单
public void SetOrder(Command command)
{
this.command = command;
}
//通知执行
public void Notify()
{
command.ExcuteCommand();
}
}
客户端代码
package Test23;
public class Program {
public static void main(String[] args) {
//开店前的准备
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter();
//开门营业
girl.SetOrder(bakeMuttonCommand1);
girl.Notify();
girl.SetOrder(bakeMuttonCommand2);
girl.Notify();
girl.SetOrder(bakeChickenWingCommand1);
girl.Notify();
}
}
上面的写法还不够完善 比如真是的情况并不是用户点一个菜 服务员就通知厨房去做一个 应该是点完烧烤后 服务员一次通知制作 还有到底点了哪些需要记录日志 以备收费 也有可能客户点多了而取消一些还没有制作的肉串
下面我们继续进行重构
服务员类
package Test23;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
//服务员
public class Waiter {
private List<Command> orders = new ArrayList<Command>();
Date date = new Date();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss");
//设置订单
public void SetOrder(Command command)
{
if (command.getClass().getName() == "Test23.BakeChickenWingCommand")
{
System.out.println("服务员:鸡翅没有了,请点别的烧烤。");
}
else
{
orders.add(command);
System.out.println("增加订单:" + command.getClass().getName() + " 时间:" + dateFormat.format(date));
}
}
//取消订单
public void CancelOrder(Command command)
{
orders.remove(command);
System.out.println("取消订单:" + command.getClass().getName() + " 时间:" + dateFormat.format(date));
}
//通知全部执行
public void Notify()
{
for (Command cmd : orders)
{
cmd.ExcuteCommand();
}
}
}
客户端代码
package Test23;
public class Program {
public static void main(String[] args) {
//开店前的准备
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter();
//开门营业 顾客点菜
girl.SetOrder(bakeMuttonCommand1);
girl.SetOrder(bakeMuttonCommand2);
girl.SetOrder(bakeChickenWingCommand1);
//点菜完闭,通知厨房
girl.Notify();
}
}
其它类不变 上面的写法就是命令模式了
命令模式 将一个请求封装为一个对象 从而使你可用不同的请求对客户进行参数化 对请求排队或记录请求日志 以及支持可撤销的操作
命令模式的优点 第一 它能较容易地设计一个命令队列 第二 在需要的情况下 可以较容易地将命令记入日志 第三 允许接收请求的一方决定是否要否决请求 第四 可以容易地实现对请求的撤销和重做 第五 由于加进新的具体命令类不影响其他的类 因此增加新的具体命令类很容易 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开
敏捷开发原则告诉我们 不要为代码添加基于猜测的 实际不需要的功能 如果不清楚一个系统是否需要命令模式 一般就不要着急去实现它 事实上 在需要的时候通过重构实现这个模式并不困难 只有在真正需要如撤销/恢复操作等功能时 把原来的代码重构为命令模式才有意义