重学设计模式(三设计模式-命令模式)

Posted 穆瑾轩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重学设计模式(三设计模式-命令模式)相关的知识,希望对你有一定的参考价值。

1、命令模式

    在前面我们学习的设计模式中,一般的调用方式就是,客户端发出请求,服务端立即做出响应。这里的请求者与方法的实现者之间都存在紧密的耦合关系,我们无法控制方法执行的时机与上下文。我们的命令模式就是用来将请求或简单的操作转换为对象的行为的。

1.1、什么是命令模式

  • 定义

    命令模式是一种行为设计模式,它将请求转换为包含有关请求信息的独立对象,将请求和与执行请求的职责分离,方便对请求进行储存、传递、调用的管理,使其可以对请求进行排队、存储请求历史记录或撤销请求等操作。

    命令模式可以存储请求对象信息,这些对象可以作为方法参数传递、延迟或排队去执行,所以也可以定位到之前的操作。举个例子:

    去餐馆吃饭,顾客找服务员点菜,每一个顾客的点菜都是一个请求,服务员将点菜信息传递给厨师,服务员并不关心菜如何做、谁来做,当顾客一多,就会出现排队的现象,这时候服务员(顾客点菜)和厨师(做菜)各司其职,井井有条才能控制整体的效率。

命令模式的结构:

    1)接收者(Receiver)角色:包含一些业务逻辑,是具体命令对象业务的真正实现者;

    2)抽象命令类(Command)角色:命令接口通常只声明一个执行命令的方法;

    3)具体命令类(Concrete Command)角色:实现各种请求,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作;

    4)请求者/调用者(Invoker)角色:请求的发送者,通过命令对象来执行请求,包含一些命令对象的引用的信息。它不需要知道接受者是谁,因此只与抽象命令类之间存在关联;

    5)客户类(Client)角色:在客户类需要创建调用者对象、具体命令类对象,在创建具体命令对象是指定对应的接收者,发送者和接受者额之间没有直接的关系,都是通过命令对象间接调用的。

    命令模式的意图在于:将一个请求封装为一个对象,将请求和与执行请求的职责分离,方便对请求进行储存、传递、调用的管理。

1.2、命令模式的优缺点

  • 优点

1)符合单一职责原则,将请求与执行请求的职责分离;

2)可以掌握操作的控制权,实现操作的延迟、撤销等功能;

3)符合开闭原则,可以在不破坏现有代码的情况下引入新的命令;

4)可以实现宏命令,将一组简单的命令组合成一个复杂的命令。

  • 缺点

发送者和接收者之间引入了一个全新的层,因此代码可能会变得更加复杂。

1.3、创建方式

我们就以餐馆点菜为例。

1)接收者

//接收者,真正的命令执行者
public class CookReceiver 
	
    void actionCooking()
    	System.out.println("已烹饪完成");
     

2)抽象命令类、具体命令类

//抽象命令类
public interface HotelCommand 

	abstract void order(); //点菜

//具体命令类:宫保鸡丁
class ConcreteChicken implements HotelCommand
	
	private CookReceiver receiver; //命令的真正执行者
	
	public ConcreteChicken()
		receiver = new CookReceiver();
	
	@Override
	public void order() 
		System.out.println("宫保鸡丁");
		System.out.println("加辣");
		receiver.actionCooking();
	
	


//具体命令类:蛋炒饭
class ConcreteRiceWithEgg implements HotelCommand
	
	private CookReceiver receiver; //命令的真正执行者
	
	public ConcreteRiceWithEgg()
		this.receiver = new CookReceiver();
	
	@Override
	public void order() 
		System.out.println("蛋炒饭");
		receiver.actionCooking();
	
	

3)调用者

//真正的调用者,服务员,也就是接收客户信息并把信息传递给接受者厨师的人
public class WaiterInvoker 

	private HotelCommand commond;
	
	private List<HotelCommand> commondList;
	
	public WaiterInvoker(HotelCommand commond) //单个单个点
		this.commond = commond;
	
	
    public WaiterInvoker(List<HotelCommand> commondList) //多个一起点
		this.commondList = commondList;
	
    
    public void callCooking()
    	commond.order();
    
    
    public void callCookingList()
    	if(commondList==null)return;
    	for (HotelCommand hc : commondList) 
			hc.order();
		
    

4)客户类

public class Client 

	public static void main(String[] args) 
		//命令
		HotelCommand hc = new ConcreteChicken();
		//我们发现使用:hc.order()或者new CookReceiver()也能达到相同的效果
		//而我们使用命令模式,对单个的请求,与直接调用的区别似乎不大
		//但是目的确实不一样的,命令模式加了个中间层,有WaiterInvoker去传递命令
		WaiterInvoker wi = new WaiterInvoker(hc);
		wi.callCooking();
		System.out.println("------------------");
		
		//当请求较多的时候,其实就比较明显了,有个排队处理的效果
		HotelCommand hc1 = new ConcreteRiceWithEgg();
		List<HotelCommand> all = new ArrayList<HotelCommand>();
		all.add(hc);
		all.add(hc1);
		
		WaiterInvoker wi1 = new WaiterInvoker(all);
		wi1.callCookingList();
		
	

此时的UML关系图:

1.4、总结及建议

    使用命令模式,可以将任何操作转换为对象。这种转换使得我们可以推迟操作的执行、对其进行排队、存储命令的历史记录、将命令发送到远程服务等。

应用场景:

    1)当你想要将操作参数化对象时,可以使用命令模式将特定的方法调用变成一个独立的对象;

    2)当你想要实现可逆操作时,可以使用命令模式。比如:数据库的事务机制就是命令模式的应用;

    3)当系统要执行一组操作时,命令模式可以定义宏命令来实现该功能。

JDK中命令模式的应用:

    java.lang.Runnable

    javax.swing.Action

以上是关于重学设计模式(三设计模式-命令模式)的主要内容,如果未能解决你的问题,请参考以下文章

重学设计模式(三设计模式-原型模式)

重学设计模式(三设计模式-单例模式)

重学设计模式(三设计模式-迭代器模式)

重学设计模式(三设计模式-迭代器模式)

重学设计模式(三设计模式-责任链模式)

重学设计模式(三设计模式-责任链模式)