【设计模式】-命令模式学习(一)
目录
2、命令类:对服务员手上的“小册子”的抽象,记录点餐要求,支持用户撤销。
3、厨师类:具体执行类,不同类型的厨师做不同的菜,减少厨师切换成本;
4、 客户端:代表用户发来请求,用户只说自己需要什么,不需要知道烤串、炒菜谁来做。
命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作[DP]。
背景:
最近在学习设计模式-命令模式,参考 《大话设计模式》,里面讲到了小菜和大鸟吃烧烤的故事:他们先去了烧烤摊,烧烤摊接待客户和烤串者都是同一个人,问题频出:烤错了、烤少、结错账……于是他们转战到烧烤店,烧烤店服务员接待他们,用“小册子”记录他们点餐,错了还能划掉,最后顺利吃烤串,账单正确。
原文如下:


我心想:我也爱吃烧烤,把我自己吃烧烤的经验用命令模式实现一遍,让编程也融入我的生活吧,哈哈!说干就干!
我的场景:
来到某烧烤店,
我:“服务员,来两串牛肉串,两串羊肉串,一个炒方便面... ...”。
服务员:在“小册子”上写下:”牛肉串✖️2,羊肉串✖️2,炒方便面 ✖️1 ... ...”。
服务员去后厨给炒菜师傅 炒菜菜单;通知烤串师傅烤串菜单。
类图设计:

代码实现:
import java.util.ArrayList;
import java.util.List;
public class Waiter {
// 维护命令列表,聚合关系
private List<Command> commands = new ArrayList<>();
// 添加命令
// 服务员根据 name 和 number 创建 command,根据 name区分烤串还是炒菜,根据 number区分是几道菜,创建 command
public void addCommand(String name,int number) {
if(name.contains("烤")){
commands.add(new BakeCommand(name,number,new BakeCook()));
}else {
commands.add(new FryCommand(name,number,new FryCook()));
}
}
public void notifyExecute() {
System.out.println("开始通知后厨");
for (Command command : commands) {
command.execute();
}
public abstract class Command {
private String name; // 菜名
private int number; // 数量
private Cook cook; // 厨师
// 构造方法 - Command 聚合 Cook
// 创建 command 的时候,服务员把厨师聚合进来,服务员知道什么类型的菜交给什么厨师做
public Command(String name, int number, Cook cook){
this.name = name;
this.number = number;
this.cook = cook;
}
public String getName() {
return name;
}
public int getNumber() {
return number;
}
public void execute(){
cook.bake(this);
}
}
public class BakeCommand extends Command{
public BakeCommand(String name, int number, Cook cook){
super(name, number, cook);
}
}
public class FryCommand extends Command {
public FryCommand(String name, int number, Cook cook) {
super(name, number, cook);
}
}
public interface Cook {
public void bake(Command command);
}
public class BakeCook implements Cook{
@Override
public void bake(Command command) {
System.out.println("[烧烤厨房]:" + command.getNumber() + "个" +command.getName() + " " + "开始烤啦!");
}
}
public class FryCook implements Cook{
@Override
public void bake(Command command) {
System.out.println("[炒菜厨房]:" + command.getNumber() + "个" +command.getName() + " " + "开始做啦!");
}
}
public class Client {
public static void main(String[] args) {
// 来了一个顾客,开始下单
System.out.println("顾客开始下单");
Waiter waiter = new Waiter();
waiter.addCommand("烤羊肉串",2);
waiter.addCommand("烤牛肉串",2);
waiter.addCommand("烤油边",2);
waiter.addCommand("炒方便面",1);
waiter.addCommand("田园大拌菜",1);
waiter.notifyExecute();
}
}
运行结果:
顾客开始下单
开始通知后厨
[烧烤厨房]:2个烤羊肉串 开始烤啦!
[烧烤厨房]:2个烤牛肉串 开始烤啦!
[烧烤厨房]:2个烤油边 开始烤啦!
[炒菜厨房]:1个炒方便面 开始做啦!
[炒菜厨房]:1个田园大拌菜 开始做啦!
解读:
1、服务员类:
1)维护命令列表:private List commands = new ArrayList<>();
2)接受订单;
3)维护“映射”, 按菜品名查表创建 Command :知道烤串和炒菜分别让哪个师傅做;因此服务员持有对 BakeCook 和 FryCook的引用;
4)撤销订单;
5)将客户与厨师解耦
6)通知厨师制作;
2、命令类:对服务员手上的“小册子”的抽象,记录点餐要求,支持用户撤销。
用多态区分不同命令类型。
3、厨师类:具体执行类,不同类型的厨师做不同的菜,减少厨师切换成本;
1)做饭;
4、 客户端:代表用户发来请求,用户只说自己需要什么,不需要知道烤串、炒菜谁来做。
总结:
1、什么是命令模式?
自己写完实现后,内心产生了疑惑:到底什么是命令模式?关键代码是什么?
某天,听米老师讲完面向对象后,我懂了:命令模式就是将请求封装成对象去处理。
我兴高采烈的向米老师求证,老师肯定了我的答案,并抛出了另一个问题:为什么这样做?(只知道结论不行),我再次陷入沉思🤔。
2、梳理需求——>确定类——>画类图——>根据类图写代码。
早在 10 年前,米老师就教我正确的开发顺序,直到这次才算清清楚楚实现,认真思考了每个类的生命周期。
3、代码如人生
为什么我决定自己实现一遍命令模式呢?因为第一遍看完书,我觉得书上写的不对。客户端怎么能知道哪个厨师去执行命令呢?我去吃烧烤从不关注哪个厨师做饭,应该是服务员关注啊。于是,在我设计的类图中:client 只知道 waiter,其他什么都不知道;由waiter 依赖 cook,解决哪个菜由谁做的问题。
但我想能写出这么好的书的作者,这么写一定有他的道理,道理是什么呢?终于,实现完自己的设计后我想明白了:我可以知道哪个厨师在做饭或者说我可以指定厨师。比如,一个烧烤店:老板和厨师都是同一个人,那我就知道是谁做饭了;或者说,小王师傅的腌笃鲜一绝,目前在某五星大饭店任职,你去饭店说:我要吃小王师傅做的腌笃鲜。对应到代码是什么呢?就是,调用方指定某个类去执行对应的业务。
遗留问题:
1、waiter还是"单一职责"的服务员吗?
2、服务员把“烤”写错了,烤串还能吃上吗?
我们留在下一篇博客中分析。
更多推荐
所有评论(0)