披萨店的生意越来越好
小明公司之前给披萨店做了一个项目,主要用了工厂模式,披萨店的老板非常满意,这不,由于披萨店的生意越来越好,小明他们公司又接到了披萨店的新需求。
披萨店招了一名服务员,专门负责顾客的下单,主要职责就是将顾客点的菜单交给做披萨的师傅,然后师傅将披萨做好。现在需要有一套程序来模拟这个场景,小明这次很开心,因为他知道这种就是一个很明确的命令模式的场景:对于顾客来说,每个人点的披萨是不一样的,他们并不关心厨师做披萨的过程,而主要在意是自己的需求能够明确到厨师那里。而服务员主要做的就是将顾客的想法传单到厨师那里,然后一个简单的命令模式就出现了:
芝士披萨厨师:
public class CheesePizzaChef {
public void createCheesePizza(){
// 做一个芝士披萨
}
}
榴莲披萨厨师:
public class DurianPizzaChef {
public void createDurianPizza(){
// 做一个榴莲披萨
}
public void sendGift(){
// 送一个礼物
}
}
抽象命令:
public interface Order {
public void execute();
}
点一个芝士披萨:
public class CheesePizzaOrder implements Order{
CheesePizzaChef chef;
public CheesePizzaOrder(CheesePizzaChef chef){
this.chef = chef;
}
public void execute(){
chef.createCheesePizza();
}
}
点一个榴莲披萨:
public class DurianPizzaOrder implements Order{
DurianPizzaChef chef;
public DurianPizzaOrder(DurianPizzaChef chef){
this.chef = chef;
}
public void execute(){
chef.createDurianPizza();
chef.sendGift();
}
}
服务员:
public class Service {
Order order;
public Service(){
}
public void setOrder(Order order){
this.order = order;
}
public void orderUp(){
order.execute();
}
}
顾客:
public class Customer {
public static void main(String[] args){
Service sevice = new Service();
//点一个芝士披萨
CheesePizzaChef cheesePizzaChef = new CheesePizzaChef();
CheesePizzaOrder cheesePizzaOrder = new CheesePizzaOrder(cheesePizzaChef);
service.setOrder(cheesePizzaOrder);
service.orderUp();
//又点了一个榴莲披萨
DurianPizzaChef durianPizzaChef = new DurianPizzaChef();
DurianPizzaOrder durianPizzaOrder = new DurianPizzaOrder(durianPizzaChef);
service.setOrder(durianPizzaChef);
service.orderUp();
}
}
顾客先点了一个芝士披萨,又点了一个榴莲披萨,可以看到,对于顾客而言,他只用了服务员做了点餐(setOrder()方法),而服务员和厨师之前也没有过多的耦合,服务员就只是再执行了orderUp()方法,然后接下来的的事顾客和服务员就再也没有关心了,那么可以看到里面有很重要的一个类就是实现Order接口的两个类:CheesePizzaOrder和DurianPizzaOrder,他们就被称为命令,命令里面包含了真正的接收者,对于这两个命令,他们的接收者分别是CheesePizzaChef和DurianPizzaChef,也就是最终做事的对象,服务员每次都设置了不同的命令,然后在orderUp()方法了调用命令的execute()方法,命令实现类对execute()做了不同实现,在CheesePizzaOrder里面是调用CheesePizzaChef做了一个芝士披萨,DurianPizzaOrder是DurianPizzaChef做了一个榴莲披萨,而且还送了一个礼物。我们注意到,在两个Order的实现类里,execute()方法用不同的类做了不同的事情,这也是我们命令模式很重要的一点,请求发出者不用关心最终的接收者到底做了什么,任何类都可以接收者,所以命令的接收者彼此之间是独立的(上面的两个厨师并没有关联,我们的接接收者还可以是老板、经理等店里任何可以为顾客服务的人),顾客和最终的接收者之间的桥梁是我们服务员中的命令对象
,也就是CheesePizzaOrder和DurianPizzaOrder,这样让顾客和接收者之间是松耦合,同样服务员和接收者之间也是松耦合。
定义
命令模式将“请求”封装成对象,以便使用不同的请求、队列、或者日志来参数化其他对象。
角色
- 抽象命令 Command
- 具体命令 ConcreteCommand
- 接收者 Receiver
- 要求命令对象执行请求 Invoker
- 创建命令者 Client
接收者不是必须的,具体的命令也可以自己处理一些事
优缺点
优点:
- 降低对象之间的耦合度。
- 新的命令可以很容易地加入到系统中。
- 可以比较容易地设计一个组合命令。
- 调用同一方法实现不同的功能
缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用
命令模式的具体使用
对象命令模式的定义大家可能还是很模糊,那我们就再仔细看看命令的使用,加深一下对命令模式的理解。接下来我们实现一个空调遥控器调节风力的功能:
空调:
public class AirConditioner {
public static final int HIGH = 3;
public static final int MEDIUM = 2;
public static final int LOW = 1;
public static final int OFF = 0;
String name;
int speed = 0;
public AirConditioner(String name){
this.name = name;
}
public void high(){
// 设置强风
speed = HIGH;
}
public void medium(){
// 设置低风
speed = MEDIUM;
}
public void low(){
// 设置弱风
speed = LOW;
}
public void off(){
// 空调关
speed = OFF;
}
public int getSpeed(){
return speed;
}
}
抽象命令:
public interface Command {
public void execute(AirConditioner airConditioner);
public void undo();
}
强风命令:
public class ConditionerHighCommand implements Command{
AirConditioner airConditioner;
int speed;
public void execute(AirConditioner airConditioner){
speed = airConditioner.getSpeed();
this.airConditioner = airConditioner;
airConditioner.high();
}
public void undo(){
if(speed = AirConditioner.HIGH)
airConditioner.high();
else if (speed = AirConditioner.MEDIUM)
airConditioner.medium();
else if (speed = AirConditioner.LOW)
airConditioner.low();
else if (speed = AirConditioner.OFF)
airConditioner.off();
}
}
弱风命令:
public class ConditionerLowCommand implements Command{
AirConditioner airConditioner;
int speed;
public void execute(AirConditioner airConditioner){
speed = airConditioner.getSpeed();
this.airConditioner = airConditioner;
airConditioner.low();
}
public void undo(){
if(speed = AirConditioner.HIGH)
airConditioner.high();
else if (speed = AirConditioner.MEDIUM)
airConditioner.medium();
else if (speed = AirConditioner.LOW)
airConditioner.low();
else if (speed = AirConditioner.OFF)
airConditioner.off();
}
}
关闭命令:
public class ConditionerLowCommand implements Command{
AirConditioner airConditioner;
int speed;
public void execute(AirConditioner airConditioner){
speed = airConditioner.getSpeed();
this.airConditioner = airConditioner;
airConditioner.off();
}
public void undo(){
if(speed = AirConditioner.HIGH)
airConditioner.high();
else if (speed = AirConditioner.MEDIUM)
airConditioner.medium();
else if (speed = AirConditioner.LOW)
airConditioner.low();
else if (speed = AirConditioner.OFF)
airConditioner.off();
}
}
空命令:
public class NoCommand implements Command{
public void execute(AirConditioner airConditioner){
// 什么也不做
}
public void undo(){
// 什么也不做
}
}
遥控器:
public class RemoteControl {
// 记录最后一次命令,首次为空命令
Command lastCommand = new NoCommand();
//遥控器上一共有4个按钮:除了撤销,其他按钮是可以自由让它有特定的功能
Command[] commands;
public RemoteControl(Command[] commands){
commands = commands;
}
public void onePressed(AirConditioner airConditioner){
commands[0].execute(airConditioner);
lastCommand = commands[0];
}
public void twoPressed(AirConditioner airConditioner){
commands[1].execute(airConditioner);
lastCommand = commands[1];
}
public void threePressed(AirConditioner airConditioner){
commands[2].execute(airConditioner);
lastCommand = commands[2];
}
// 撤销按钮被按
public void undoPressed(){
lastCommand.undo();
}
}
人:
public class People {
public static void main(String[] args){
Command[] commands = new Command[3];
commands[0] = new ConditionerHighCommand();
commands[1] = new ConditionerLowCommand();
commands[2] = new ConditionerOffCommand();
// 暂且给三个按钮赋予这三个相应的功能,强风、弱风、关闭功能
RemoteControl rc = new RemoteControl(commands);
AirConditioner airConditioner = new AirConditioner("客厅的空调");
rc.twoPressed(airConditioner);
rc.onePressed(airConditioner);
rc.twoPressed(airConditioner);
rc.undoPressed(airConditioner);
// 最终风速为?
}
}
我们可以按遥控器了,每一次按了以后,接收者空调会做出相应的处理(除了撤销按钮),处理由被赋予按钮的命令决定,而且我们还可以更换三个按钮为其他功能,比如已经有的一个命令:空命令,如果实例化空调时,给其中一个按钮赋予空命令职责,他将什么都不会做,空命令在也是一个有非常大用处的命令。不过这个遥控器的还有很多需要优化的地方,比如撤销按钮,目前看来,撤销只能撤销前一次操作(恢复到前一次操作后的状态),大家可以再优化一下,看看怎么实现连续撤销功能,这也和命令模式的一个很重要的作用类似:实现事务系统。
还有哪些用法
队列请求
命令模式可以用来实现一个异步耗时队列请求的功能,我们可以实现一些列不同的命令,这些命令可能是某一个功能,也可能是一组功能,我们创建很多不同的命令,把他们放进一个队列,然后可能会有另一个线程从队列里面取出这些命令,挨个去执行他们。
状态恢复
当我们程序执行一些命令,我们可以将每一次的执行的命令写进我们的日志,当电脑死机重启后,我们将日志信息读取出来,然后再挨个的去执行这些命令,让电脑恢复到之前死机时候的状态。