命令模式
定义
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
解决
在系统中,行为请求行为者通常是一种紧密结合的实现,但在某些情况下,需要对行为进行记录、撤销关系或重做、事务处理等时,这种无法变化的紧耦合的情况用命令模式设计就合适。
优点
- 能较容易地设计一个命令队列。
- 在需要的情况下,可以较容易地将命令记入日志。
- 允许接受请求的一方决定是否要否决请求。
- 可以容易地实现对请求的撤销和重做。
- 由于加紧新的具体命令类不影响其他的类,因此增加新的命令类很容易。
- 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。
缺点
- 使用命令可能会导致系统有过多的特定命令类。因为这些模式都需要设计一个特定的命令类,因此每个系统可能需要大量的类,这将影响命令的使用模式。
结构
命令模式包含如下角色:
- Command: 抽象命令类
- ConcreteCommand: 具体命令类
- Invoker: 调用者
- Receiver: 接收者
实现
package commanpattern;
import java.util.List;
/**
* 用来声明执行操作的接口
*/
public abstract class Command {
protected List<Reciever> recievers;
public Command(List<Reciever> recievers) {
this.recievers = recievers;
}
public void addRecievers(Reciever reciever) {
this.recievers.add(reciever);
}
public abstract void execute();
}
/**
* 将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现execute
*/
class ConcreteCommand extends Command {
public ConcreteCommand(List<Reciever> recievers) {
super(recievers);
}
@Override
public void execute() {
for (Reciever reciever : recievers) {
reciever.action();
}
}
}
package commanpattern;
/**
* 要求该命令执行这个请求
*/
public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
package commanpattern;
/**
* 知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者。真正执行请求的地方!
*/
interface Reciever {
public void action();
}
class RecieverA implements Reciever {
@Override
public void action() {
System.out.println("RecieverA执行请求!");
}
}
class RecieverB implements Reciever {
@Override
public void action() {
System.out.println("RecieverB执行请求!");
}
}
class RecieverC implements Reciever {
@Override
public void action() {
System.out.println("RecieverC执行请求!");
}
}
package commanpattern;
import java.util.ArrayList;
import java.util.List;
/**
* 创建一个具体命令对象并设定它的接收者
*/
public class CommandClient {
public static void main(String[] args) {
List<Reciever> recievers = new ArrayList<Reciever>();
recievers.add(new RecieverA());
recievers.add(new RecieverB());
recievers.add(new RecieverC());
Command command = new ConcreteCommand(recievers);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
实例
package commanpattern.demo;
/**
* 抽象命令类,只需要确定“烤肉串者”是谁
*/
public abstract class Command {
protected Barbecuer reciever;
public Command(Barbecuer reciever) {
this.reciever = reciever;
}
public abstract void executeCommand();
@Override
public abstract String toString();
}
//烤羊肉串命令 具体命令类
class BakeMuttonCommand extends Command{
public BakeMuttonCommand(Barbecuer reciever) {
super(reciever);
}
@Override
public void executeCommand() {
reciever.bakeMutton();
}
@Override
public String toString() {
return "一个羊肉";
}
}
//烤鸡肉命令 具体命令类
class BakeChickenCommand extends Command{
public BakeChickenCommand(Barbecuer reciever) {
super(reciever);
}
@Override
public void executeCommand() {
reciever.bakeChicken();
}
@Override
public String toString() {
return "一个鸡肉";
}
}
package commanpattern.demo;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* invoker调用者
*/
public class Waiter {
private List<Command> orders = new ArrayList<>();
//设置订单
public void setOrder(Command command)
{
//这里一个判断条件,用来否决
if (command.toString()=="asdasd")
{
System.out.println("鸡翅没有了");
}
else
{
orders.add(command);
//日志
System.out.println("增加订单:"+command.toString()+"\t时间:"+ Calendar.getInstance().getTime());
}
}
//取消订单
public void cancelOrder(Command command)
{
orders.remove(command);
//日志
System.out.println("取消订单:"+command.toString()+"\t时间:"+ Calendar.getInstance().getTime());
}
public void notifyCommand()
{
for(Command command : orders)
{
command.executeCommand();
}
}
}
package commanpattern.demo;
public class Barbecuer {
//烤羊肉 recivever接收者
public void bakeMutton()
{
System.out.println("烤个羊肉");
}
//烤羊肉
public void bakeChicken()
{
System.out.println("烤个鸡肉");
}
}
package commanpattern.demo;
public class CommandClient {
public static void main(String[] args) {
//一个烤肉者 (Reciever)接收者
Barbecuer b=new Barbecuer();
//一个服务员 (Invoker)调用者
Waiter w=new Waiter();
//顾客点菜
Command bakeMuttonCommand1=new BakeMuttonCommand(b);
Command bakeMuttonCommand2=new BakeMuttonCommand(b);
Command bakeChickenCommand1=new BakeChickenCommand(b);
w.setOrder(bakeMuttonCommand1);
w.setOrder(bakeMuttonCommand2);
w.setOrder(bakeChickenCommand1);
//点菜完毕,Invoker去通知Reciever做事情
w.notifyCommand();
}
}
总结
- 在命令模式中,将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作模式或事务模式。
- 命令模式包含四个角色:抽象命令类中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作;具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中;调用者即请求的发送者,又称为请求者,它通过命令对象来执行请求;接收者执行与请求相关的操作,它具体实现对请求的业务处理。
- 命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
- 命令模式的主要优点在于降低系统的耦合度,增加新的命令很方便,而且可以比较容易地设计一个命令队列和宏命令,并方便地实现对请求的撤销和恢复;其主要缺点在于可能会导致某些系统有过多的具体命令类。
- 命令模式适用情况包括:需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互;需要在不同的时间指定请求、将请求排队和执行请求;需要支持命令的撤销操作和恢复操作,需要将一组操作组合在一起,即支持宏命令。