与中介者的关系
就比如中介者模式那里,BangZi 在交保护费的时候,还看 GuiZi 交没交,交多少,底层横向发生联系。
代码语言:javascript复制public BangZiPayCommand extends Command {
public BangZiPayCommand(int money) {
super(money);
}
public void execute() {
// 比如 GuiZi 交多少有个方法返回
if (gz.getPayMoney() < 4) {
gz.pay(money / 2)
} else {
gz.pay(money);
}
gz.bite(false);
gz.build(false);
}
}
那这我怎么又感觉回到中介者了呢,这两个模式分界有点越来越模糊的感觉。感觉命令就是中介者的再一次封装,原来的中介类会变得臃肿,命令将中介者内部的 if/else 抽象出一种 Command,将具体逻辑挪到 Command 里。等于说将 Mediator 一个大的类拆分成许多个 Command,就是单一职责,原来的中介者职责太多,现在一个职责抽取一个 Command。
上一节中介者模式例子是这么写的
代码语言:javascript复制public abstract class AbstractMediator {
protected DengTa dt;
protected GuiZi gz;
protected BangZi bz;
// setter/getter
//中介者最重要的方法叫做事件方法,处理多个对象之间的关系
// str 是具体指令,objects 是可能调用方法需要的参数
public abstract void execute(String str,Object...objects);
}
public class Mediator extends AbstractMediator {
public void execute(String str,Object...objects){
if(str.equals("dengTa.shouBHF")){ //灯塔收保护费
this.dengTaShouBHF();
}else if(str.equals("guiZi.jiaoBHF")){ //鬼子交保护费
this.guiZiJiaoBHF();
}else if(str.equals("bangZi.jiaoBHF")){ //棒子交保护费
this.bangZiJiaoBHF();
}
}
private void dengTaShouBHF() {
gz.jiaoBHF(4); // 鬼子上交 4 千亿
bz.jiaoBHF(1); // 棒子上交 1 千亿
}
private int guiZiJiaoBHF() {
int bhf = number;
if (bz.maGuiZi()) { // 棒子骂我
if (dt.shouShi("BangZi")) { // 爸爸帮我收拾它一顿
bhf = number * 2; // 心情好,交两倍
} else { // 爸爸没有帮我收拾
bhf = number / 2; // 心情不好,少交一半
}
}
bhf = number;
gz.setBHF(bhf);
System.out.println("给爸爸交保护费:" bhf);
}
private int bangZiJiaoBHF() {
int gzBHF = gz.getBFH();
if (gzBHF == 0) {
System.out.println("他还没交,主人先从他那收到钱,我再给");
} else if (gzBHF < 4) {
System.out.println("他才交了这么一点,那我只能交保护费 " number/2);
} else {
System.out.println("给主人交保护费:" number);
}
}
}
改造一下试试,将 Mediator 抽取出来的单一职责的逻辑变成 Command,然后自己变成 Invoker。
代码语言:javascript复制// 原来的 AbstractMediator
public abstract class Command {
// 接收者 Receiver
protected DengTa dt;
protected GuiZi gz;
protected BangZi bz;
// setter/getter
// 不需要指令参数 str 了,Command 类型本身就代表了什么指令
// 我上面例子参数是通过 Command 的构造方法传进去的,这里通过
// 执行方法参数,区别不大
public abstract void execute(Object...objects);
}
// dengTa.shouBHF 变成一个具体 Command
public class DengTaShouBHFCommand extend Command {
public void execute(Object...objects) {
this.dengTaShouBHF();
}
private void dengTaShouBHF() {
gz.jiaoBHF(4); // 鬼子上交 4 千亿
bz.jiaoBHF(1); // 棒子上交 1 千亿
}
}
// guiZi.jiaoBHF 变成一个具体 Command
public class GuiZiJiaoBHFCommand extend Command {
public void execute(Object...objects) {
this.guiZiJiaoBHF();
}
private int guiZiJiaoBHF() {
int bhf = number;
if (bz.maGuiZi()) { // 棒子骂我
if (dt.shouShi("BangZi")) { // 爸爸帮我收拾它一顿
bhf = number * 2; // 心情好,交两倍
} else { // 爸爸没有帮我收拾
bhf = number / 2; // 心情不好,少交一半
}
}
bhf = number;
gz.setBHF(bhf);
System.out.println("给爸爸交保护费:" bhf);
}
}
// bangZi.jiaoBHF 同样是一个 Command
// 就是原来的 Mediator
public class InVoker {
private Command cmd;
// 通过 setter 方法
public void setCommand(Command cmd) {
this.cmd = cmd;
}
// 这方法就是原来的 execute,为了和 Command 的 execute 区分
// 写成 action,其实写成 execute 也行,
// str 参数不需要了,因为 setCommand 取代了这个功能
public void action(Object...objects){
// 原来 if/else 判断不需要了,因为逻辑分散到具体 Command 了
cmd.execute();
}
}
我去,真的感觉两者本质是一样的了。中介者是一层封装,命令是在中介者基础上再一次封装。且没什么高层低层的区别,都是一样的,即便低层跪着给高层请安,那也可以是一个 Command。
撤销
命令本身有撤销的逻辑。
代码语言:javascript复制public abstract class Command {
// 其它语句
public abstract void execute();
// 撤销方法
public abstract void undo();
}
public GuiZiPayCommand extends Command {
public GuiZiPayCommand(int money) {
super(money);
}
public void execute() {
gz.pay(money);
gz.bite(false);
gz.build(false);
}
public void undo() {
// 内部做撤销逻辑,比如原来是 build(true),bite(true) 的
gz.build(true);
gz.bite(true);
gz.back(money); // 比如还有个把钱收回来的方法
}
}
然后中间的发布者要内部记录下执行的命令,这样方便找到并撤销。
代码语言:javascript复制public class Invoker {
private List<Command> cmdList; // 记录命令列表
public void setCommand(Command command) {
cmdList.add(command);
}
public void action() {
cmdList.get(command.size()-1).execute();
}
public void unAction() { // 删掉最后一个命令,并撤销
cmdList.remove(command.size()-1).undo();
}
}
代码语言:javascript复制public class FatherDengTa() {
private Invoker invoker = new Invoker(); // 中介
public void shouGuiZiBHF() {
Command cmd = new GuiZiPayCommand(1);
invoker.setCommand(cmd);
invoker.action();
// GuiZi 欢欣鼓舞,一分钟过去了
// 说弄错了,少说一个 0,先撤销
invoker.unAction();
}
}
通过状态值撤销,适合命令本身也就是设置一些不同的状态,然后执行命令时记录下当前状态,撤销就是根据记下来的状态再回滚回去。
宏编程
就是一个命令封装若干个其它命令,然后调用它一次性执行多个。
代码语言:javascript复制public SonPayCommand extends Command {
private Command[] cmds;
// 由其它命令数组构造
public SonPayCommand(Command[] cmds) {
this.cmds = cmds;
}
public void execute() {
for (int i=0; i<cmds.length; i ) {
cmds[i].execute();
}
}
}
public class FatherDengTa() {
private Invoker invoker = new Invoker(); // 中介
// 爸爸不需要发一个命令给儿子,再发一个命令给另一个儿子
// 爸爸把命令直接打包好,一起发出去
public void shouSonBHF() {
Command cmd = new GuiZiPayCommand(4);
Command cmd2 = new BangZiPayCommand(1);
Command[] cmds = { cmd, cmd2 };
SonPayCommand sonCmd = new SonPayCommand(cmds);
invoker.setCommand(sonCmd);
invoker.action();
}
}
其它应用
- 队列请求:由于 Command 内部已经封装了接收者和具体执行的动作,所以可以用命令封装后放入一个队列,其它线程读取队列,取出 Command,调用它的 execute,接收者比如 GuiZi 就自己把事情办了。
- 日志请求:还是由于 Command 封装了接收者和动作的特性,一些操作过程中,每次都记录一个 Command 当做日志,如果系统死机了,就可以从日志中取出命令,再一次一次的执行,就能够恢复到死机前的状态。如果倒过来,一个一个撤销,就可以恢复到之前的某种状态。