**
设计模式(十三)之命令模式
**
-
案例说明
我们开了一家水果捞专卖店,老板是想把原先的纸质菜单换成一个类似电子遥控器样式的菜单(如下图)。店里现只有三种水果——苹果、香蕉、芒果,顾客需要加哪种水果就点哪个,这里我们就用命令模式来解决这个需求。
命令模式一般有三大角色,下达命令者、接收命令者、命令,上图中顾客就是下达命令者,水果就是接收命令者,命令就是顾客的需要与不需要的操作。命名模式使得下达命令者与接收命令者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
我们将命令设为一个接口,接口下有三个实现的子类——点苹果的命令、点香蕉的命令和点芒果的命令,各个子类命令将各自的水果组合进去,相当于在命令类中调用水果里面的方法。菜单类将命令接口组合进去,调用菜单里面的按钮方法相当于调用命令里面的方法。水果实体类
public class Apple {
// 需要加苹果
public void order(){
System.out.println("水果捞里加苹果");
}
// 不需要加苹果
public void noOrder(){
System.out.println("水果捞里不加苹果");
}
}
public class Banana {
// 需要加香蕉
public void order(){
System.out.println("水果捞里加香蕉");
}
// 不需要加香蕉
public void noOrder(){
System.out.println("水果捞里不加香蕉");
}
}
public class Mango {
// 需要加芒果
public void order(){
System.out.println("水果捞里加芒果");
}
// 不需要加芒果
public void noOrder(){
System.out.println("水果捞里不加芒果");
}
}
命令接口
public interface Command {
// 执行需要操作
void execute();
// 执行不需要操作
void undo();
}
命令实现子类
public class AppleOrderCommand implements Command{
private Apple apple;
public AppleOrderCommand(Apple apple){
this.apple = apple;
}
// 执行需要苹果的操作
@Override
public void execute() {
apple.order();
}
// 执行不需要苹果的操作
@Override
public void undo() {
apple.noOrder();
}
}
public class BananaOrderCommand implements Command{
private Banana banana;
public BananaOrderCommand(Banana banana){
this.banana = banana;
}
// 执行需要香蕉的操作
@Override
public void execute() {
banana.order();
}
// 执行不需要香蕉的操作
@Override
public void undo() {
banana.noOrder();
}
}
public class MangoOrderCommand implements Command{
private Mango mango;
public MangoOrderCommand(Mango mango){
this.mango = mango;
}
// 执行需要芒果的操作
@Override
public void execute() {
mango.order();
}
// 执行不需要芒果的操作
@Override
public void undo() {
mango.noOrder();
}
}
菜单类,默认按钮为不需要的状态,给每一排的按钮设置相应的含义,给各个按钮设置相应的方法。
public class Menu {
// 模拟遥控器上的三个按钮
private Command[] orderCommands;
public Menu(){
orderCommands = new Command[3];
// 第一排按钮是点苹果的按钮
orderCommands[0] = new AppleOrderCommand(new Apple());
// 第二排按钮是点香蕉的按钮
orderCommands[1] = new BananaOrderCommand(new Banana());
// 第三排按钮是点芒果的按钮
orderCommands[2] = new MangoOrderCommand(new Mango());
// 默认按钮是不需要的状态
for (int i = 0; i < 3; i++) {
orderCommands[i].undo();
}
System.out.println("——————————————————————————————————————————————————————");
}
public void pushOrderBotton(int number){
// 执行第number排按钮上的需要操作
orderCommands[number].execute();
}
public void pushNoOrderBotton(int number){
// 执行第number排按钮上的不需要操作
orderCommands[number].undo();
}
}
客户端
public class Client {
public static void main(String[] args) {
Menu menu = new Menu();
// 点击第二排需要的按钮
menu.pushOrderBotton(1);
// 点击第三排需要的按钮
menu.pushOrderBotton(2);
// 点击第二排不需要的按钮
menu.pushNoOrderBotton(1);
}
}
测试,会先打印出默认设置的结果,再打印操作的历史记录。
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=61688:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar;D:\dev_tools\repository\cglib\cglib\3.3.0\cglib-3.3.0.jar;D:\dev_tools\repository\org\ow2\asm\asm\7.1\asm-7.1.jar com.wd.command.Client
水果捞里不加苹果
水果捞里不加香蕉
水果捞里不加芒果
——————————————————————————————————————————————————————
水果捞里加香蕉
水果捞里加芒果
水果捞里不加香蕉
Process finished with exit code 0
- 总结
1、将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:请求发起者和请求执行者之间的解耦是通过命令对象实现的,命令对象起到 了纽带桥梁的作用;
2、可以设计一个命令队列,只要把命令对象放到列队,就可以多线程的执行命令;
3、命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这
点在在使用的时候要注意。