应用场景:
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
模式结构:
Command:定义命令的统一接口
ConcreteCommand:Command接口的实现者,用来执行具体的命令,某些情况下可以直接用来充当Receiver。
Receiver:命令的实际执行者
Invoker:命令的请求者,是命令模式中最重要的角色。这个角色用来对各个命令进行控制。
代码案例一:
话说春秋战国时代,大将军想派某军队夜袭某城,却又不能自己去通知,于是将命令下达了下去。
首先,创建请求者类(大将军)。
public class ArmySuperior {
Command command;//创建命令的引用
//用来存放具体命令的引用
public void setCommand(Command command){
this.command = command;
}
//让具体命令执行execute()方法,偷袭敌人
public void startExecuteCommand(){
command.execute();
}
}
接着,创建创建接受者,也叫执行者(某军队)。
public class CompanyArmy {
//接受者执行如何偷袭敌人
public void sneakAttack(){
System.out.println("我们知道如何偷袭敌人,保证完成任务!!!");
}
}
重点:请求者与接收者完全松耦合,这是命令模式的特点。
创建命令接口。
public interface Command {
public abstract void execute();
}
创建命令接口的实现类。
public class ConcreteCommand implements Command {
CompanyArmy army; //创建接受者引用
ConcreteCommand(CompanyArmy army){
this.army = army;
}
//封装指挥官的请求
@Override
public void execute(){
army.sneakAttack(); //偷袭敌人
}
}
最后,main方法所在的类。
public class OneApplication {
public static void main(String[] args) {
//接收者
CompanyArmy companyArmy = new CompanyArmy();
//命令实现类,将接收者传入
ConcreteCommand concreteCommand = new ConcreteCommand(companyArmy);
ArmySuperior superior = new ArmySuperior();
superior.setCommand(concreteCommand);
superior.startExecuteCommand();
}
}
执行结果:
代码案例二:
某商人想让服务人员帮忙买卖股票,这时就有买和卖两个动作了,就需要两个命令实现类了。
首先还是请求者与接受者类。
/*请求者类*/
public class Broker {
private List<Order> orderList = new ArrayList<Order>();
public void takeOrder(Order order){
orderList.add(order);
}
public void placeOrders(){
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
/**接收者*/
public class Stock {
public void buy(){
System.out.println("Stock bought");
}
public void sell(){
System.out.println("Stock sold");
}
}
接着,创建命令接口。
public interface Order {
void execute();
}
创建两个操作的命令实现类。
public class BuyStock implements Order{
private Stock abcStock;
public BuyStock(Stock abcStock){
this.abcStock = abcStock;
}
@Override
public void execute() {
abcStock.buy();
}
}
public class SellStock implements Order{
private Stock abcStock;
public SellStock(Stock abcStock){
this.abcStock = abcStock;
}
@Override
public void execute() {
abcStock.sell();
}
}
最后,创建main方法所在的类。
public class OneApplication {
public static void main(String[] args) {
Stock stock = new Stock();
Order buyOrder = new BuyStock(stock);
Order sellOrder = new SellStock(stock);
Broker broker = new Broker();
broker.takeOrder(buyOrder);
broker.takeOrder(sellOrder);
broker.placeOrders();
}
}
我们发现,两个命令就需要两个命令实现类,这是命令模式的缺点。
命令接口除了execute()执行方法外,还可以添加撤销方法,并在命令实现类实现撤销方法,可以实现执行-撤销操作。
我们总结一下优缺点:
优点:
降低系统的耦合度:将调用操作的对象与知道如何实现该操作的对象解耦。
Command是头等的对象。它们可像其他的对象一样被操纵和扩展。
增加新的命令很容易,因为这无需改变已有的类。
可以方便地实现对请求的Undo撤销和Redo恢复。
命令模式结合其他模式会更优秀:命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少 Command子类的膨胀问题。
缺点:
因为每个命令都需要具体命令类,所以使用命令模式可能会导致某些系统有过多的具体命令类。
命令模式的其他应用:
宏命令模式:命令模式 加 组合模式,我们可以将多个命令组合到一起来实现命令的批处理。
队列请求:将命令排成一个队列打包,一个个调用 execute 方法,如线程池的任务队列,线程不关心任务队列中是读 IO 还是计算,只取出命令后执行,接着进行下一个。
日志请求:某些应用需要我们将所有的动作记录在日志中,然后在系统死机等情况出现时,重新调用这些动作恢复到之前的状态。如数据库事务。
参考链接:http://www.runoob.com/design-pattern/command-pattern.html