您的位置:首页 > 其它

设计模式之09命令模式(笔记)

2012-05-16 23:16 369 查看
1 定义:

1.1 定义:Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能)。

1.2 通用类图:



个人认为以下类图更利于理解:



类图中包含的Client从来不是用作测试的,这里的client是为了将“请求”与“执行者”对应起来,若使用此模式,则可以依次准备好:Receiver、Command、Invoker,最后直接调用Invoker的方法。所以这里,为便于理解,将其类图作以细化。

1.3 通用代码:

注意上述client的静态main方法为人为添加的测试,非原模式的一部分。

public abstract class Receiver {
		// 抽象接收者,定义每个接收者都必须完成的业务
		public abstract void action();
	}

	public class ConcreteReceiver1 extends Receiver {
		// 每个接受者都必须处理一定的业务逻辑
		public void action() {
		}
	}

	public abstract class Command {
		// 每个命令类都必须有一个执行命令的方法
		public abstract void execute();
	}

	public class ConcreteCommand1 extends Command {
		// 也对那个Receiver类进行命令处理
		private Receiver receiver;

		// 构造函数传递接收者
		public ConcreteCommand1(Receiver _receiver) {
			this.receiver = _receiver;
		}

		// 每个具体的命令都必须实现一个命令
		public void execute() {
			// 业务处理
			this.receiver.action();
		}
	}

	public class Invoker {
		private Command command;

		// 受气包,接受命令
		public void setCommand(Command _command) {
			this.command = _command;
		}

		// 执行命令
		public void doSomething() {
			this.command.execute();
		}
	}

	public class Client {
		public static Command prepareCommand() {
			// 定义接收者
			Receiver receiver = new ConcreteReceiver1();
			// 定义一个发送给接收者的命令
			Command command = new ConcreteCommand1(receiver);
			return command;
		}

		public static void main(String[] args) {
			// 首先声明出调用者Invoker
			Invoker invoker = new Invoker();
			// 把命令交给调用者去执行
			invoker.setCommand(prepareCommand());
			invoker.doSomething();
		}
	}


2 优点
2.1 类间解耦:调用者与接收者角色之间没有任何依赖关系,调用者实现功能时只须调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。

2.2 可扩展性:Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。

3 缺点

如果命令多,Command的子类就多,这个类会膨胀很大,需慎用。

4 应用场景

触发-反馈机制处理的场所。

5 注意事项

6 扩展

6.1 撤销-重做:这种涉及状态改变的情况,可以有以下两种方法解决。

A:结合备忘录模式还原最后状态,该方法适合接收者为状态的变更情况,而不适合事件处理。

B:通过增加一个新的命令,实现事件的回滚。

6.2 对于接收者不多的情形下,我们可以适当地将某一接收者作为默认参数,实在需要改变接收者时再重新设置。这样可以减少高层模块对低层模块的依赖关系,提高系统的稳定性。

7 范例

7.1撤销-重做(仅支持单步撤销的计算数字的例子)

类图如下:



源代码如下:

package _09_Command.example;
	public abstract class Command {
		Receiver receiver;
		int parm;

		public Command(Receiver rec, int parm) {
			this.receiver = rec;
			this.parm = parm;
		}

		public abstract void execute();
	}

	public class AddCommand extends Command {
		public AddCommand(Receiver rec, int parm) {
			super(rec, parm);
		}

		@Override
		public void execute() {
			receiver.action(CMD.ADD, parm);
		}
	}

	public class SubCommand extends Command {
		public SubCommand(Receiver rec, int parm) {
			super(rec, parm);
		}

		@Override
		public void execute() {
			receiver.action(CMD.SUB, parm);
		}
	}

	public class MulCommand extends Command {
		public MulCommand(Receiver rec, int parm) {
			super(rec, parm);
		}

		@Override
		public void execute() {
			receiver.action(CMD.MUL, parm);
		}
	}

	public class DivCommand extends Command {
		public DivCommand(Receiver rec, int parm) {
			super(rec, parm);
		}

		@Override
		public void execute() {
			receiver.action(CMD.DIV, parm);
		}
	}

	public class SetCommand extends Command {
		public SetCommand(Receiver rec, int parm) {
			super(rec, parm);
		}

		@Override
		public void execute() {
			receiver.action(CMD.SET, parm);
		}
	}

	public class UndoCommand extends Command {
		public UndoCommand(Receiver rec, int parm) {
			super(rec, 0);
		}

		@Override
		public void execute() {
			receiver.action(CMD.UNDO, 0);
		}
	}

	enum CMD {
		ADD, SUB, MUL, DIV, SET, UNDO
	};

	public abstract class Receiver {
		protected int result = 0;
		protected int lastParm;
		protected CMD lastAction;

		public abstract void action(CMD cmd, int parm);
	}

	public class ConcreteReceiver extends Receiver {
		@Override
		public void action(CMD cmd, int parm) {
			switch (cmd) {
			case ADD:
				System.out.print(result + " + " + parm + " = ");
				result += parm;
				lastParm = parm;
				lastAction = CMD.ADD;
				System.out.println(result);
				System.out
						.println("----------------------------------------------");
				break;
			case SUB:
				System.out.print(result + " - " + parm + " = ");
				result -= parm;
				lastParm = parm;
				lastAction = CMD.SUB;
				System.out.println(result);
				System.out
						.println("----------------------------------------------");
				break;
			case MUL:
				System.out.print(result + " x " + parm + " = ");
				result *= parm;
				lastParm = parm;
				lastAction = CMD.MUL;
				System.out.println(result);
				System.out
						.println("----------------------------------------------");
				break;
			case DIV:
				System.out.print(result + " / " + parm + " = ");
				result /= parm;
				lastParm = parm;
				lastAction = CMD.DIV;
				System.out.println(result);
				System.out
						.println("----------------------------------------------");
				break;
			case SET:
				result = parm;
				lastParm = 0;
				lastAction = CMD.SET;
				System.out.println("清除并设置值: " + result);
				System.out
						.println("----------------------------------------------");
				break;
			case UNDO:
				if (lastAction != CMD.UNDO && lastAction != CMD.SET) {
					switch (lastAction) {
					case ADD:
						result -= lastParm;
						break;
					case SUB:
						result += lastParm;
						break;
					case MUL:
						result /= lastParm;
						break;
					case DIV:
						result *= lastParm;
						break;
					}
					lastAction = CMD.UNDO;
					System.out.println("撤销上步操作后值: " + result);
					System.out
							.println("----------------------------------------------");
				}
				break;
			}
		}
	}

	public class Invoker {
		Command command;

		public void action() {
			command.execute();
		}

		public void setCommand(Command cmd) {
			command = cmd;
		}
	}

	public class Client {
		public static void main(String[] args) {// // just for test
			Receiver rec = new ConcreteReceiver();
			Command cmds[] = { new SetCommand(rec, 5), new AddCommand(rec, 5),
					new SubCommand(rec, 5), new UndoCommand(rec, 5),
					new MulCommand(rec, 5), new DivCommand(rec, 5) };
			Invoker inv = new Invoker();
			for (int i = 0; i < 6; i++) {
				inv.setCommand(cmds[i]);
				inv.action();
			}
		}
	}


结果如下:

清除并设置值: 5
----------------------------------------------
5 + 5 = 10
----------------------------------------------
10 - 5 = 5
----------------------------------------------
撤销上步操作后值: 10
----------------------------------------------
10 x 5 = 50
----------------------------------------------
50 / 5 = 10
----------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: