Java设计模式之行为型:命令模式

Java设计模式之行为型:命令模式

前言:

        在开发中,我们可能需要向某些对象发送一些请求,但我们不知道请求的具体接收者是谁,也不知道被请求的操作是哪个,只知道在系统运行中指定具体的请求接收者即可,打个比方,电视遥控器,我们只需知道按哪个按钮能够打开电视、关闭电视和换台即可,并不需要知道是怎么开电视、关电视和换台的,对于这种情况,我们可以采用命令模式来进行设计

一、定义:

        命令模式的本质是将请求封装成对象,将发出命令与执行命令的责任分开,命令的发送者和接收者完全解耦,发送者只需知道如何发送命令,不需要关心命令是如何实现的,甚至是否执行成功都不需要理会。命令模式的关键在于引入了抽象命令接口,发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。另外命令可以像强对象一样可以被存储和传递,所以可支持撤销的操作

        使用命令模式的优势在于降低了系统的耦合度,而且新命令可以很方便添加到系统中,也容易设计一个组合命令。但缺点在于会导致某些系统有过多的具体命令类,因为针对每一个命令都需要设计一个具体命令类。所以命令模式适用于以下场景:

  • (1)需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  • (2)系统需要在不同的时间指定请求、将请求排队和执行请求。
  • (3)系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
  • (4)系统需要将一组操作组合在一起,即支持宏命令。

二、UML结构图:

Java设计模式之行为型:命令模式

  • Receiver:接收者,执行命令的对象,任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
  • Command:抽象命令类,声明需要执行的方法。
  • ConcreteCommand:具体命令类,通常会持有接收者,并调用接收者的功能完成命令要执行的操作。
  • Invoker:调用者,通常会持有命令对象,可以持有多个命令对象,是客户端真正触发命令并要求命令执行相应操作的地方,就是相当于使用命令对象的入口。
  • Client:客户类,创建具体的命令对象,并且设置命令对象的接收者。注意这里不是指常规意义上的客户端,把这个 Client 称为装配者会合适,主要用于组装命令对象和接收者。

三、代码实现:

        这里以电视机为例。电视是请求的接受者,遥控器是请求的发送者,遥控器上有一些按钮,不同的按钮对应着不同的操作。在这里遥控器需要执行三个命令:打开电视机、关闭电视机、换台。UML类图如下:

Java设计模式之行为型:命令模式

抽象命令类:Command.java

/**
 * Command命令接口,为所有的命令声明一个接口。所有的命令都应该实现它
 */
public interface Command {
	public void execute();
}

电视机类:Television.java

public class Television {
	public void open(){
		System.out.println("打开电视机......");
	}
	
	public void close(){
		System.out.println("关闭电视机......");		
	}
	
	public void changeChannel(){
		
		System.out.println("切换电视频道......");
	}
}

遥控器类:Controller.java

public class Controller {
	private Command openTVCommand;
	private Command closeTVCommand;
	private Command changeChannelCommand;
	
	public Controller(Command openTvCommand,Command closeTvCommand,Command changeChannelCommand){
		this.openTVCommand = openTvCommand;
		this.closeTVCommand = closeTvCommand;
		this.changeChannelCommand = changeChannelCommand;
	}
	
	/**
	 * 打开电视剧
	 */
	public void open(){
		openTVCommand.execute();
	}
	
	/**
	 * 关闭电视机
	 */
	public void close(){
		closeTVCommand.execute();
	}
	
	/**
	 * 换频道
	 */
	public void change(){
		changeChannelCommand.execute();
	}
}

 遥控器的三个按钮:

public class OpenTvCommand implements Command{
	private Television tv;
	
	public OpenTvCommand(Television tv){
		this.tv = tv;
	}
	
	public void execute() {
		tv.open();
	}
}
public class ChangeChannelCommand implements Command{
	private Television tv;
	
	public ChangeChannelCommand(Television tv){
		this.tv = tv;
	}
	
	public void execute() {
		tv.changeChannel();
	}
}
public class CloseTvCommand implements Command{
	private Television tv;
	
	public CloseTvCommand(Television tv){
		this.tv = tv;
	}
	
	public void execute() {
		tv.close();
	}
}

客户端:Client.java

public class Client {
		public static void main(String a[])
		{
			Television tv = new Television();
                        Command openCommand,closeCommand,changeCommand;
			
			openCommand = new OpenTvCommand(tv);
			closeCommand = new CloseTvCommand(tv);
			changeCommand = new ChangeChannelCommand(tv);
			
			Controller control = new Controller(openCommand,closeCommand,changeCommand);
			
			control.open();           //打开电视机
			control.change();         //换频道
			control.close();          //关闭电视机
		}
}

运行结果:

打开电视机......
切换电视机频道......
关闭电视机......

命令模式扩展:前面说过,命令模式支持撤销命令:在电视遥控器中,我们还有这样一个按钮,那就是返回。用于切换到上面一个频道中去。在命令模式中也支持撤销操作,在这里我们只需要记录上一个频道,然后将上一个频道传入即可。在这里将 Command 进行一个简单的修改:将 execute() 改为 execute(int i);  i表示频道,用于进行频道切换。

/**
 * Command命令接口,为所有的命令声明一个接口。所有的命令都应该实现它
 */
public interface Command {
	/**
	 * 为了方便切换频道,这里使用参数i将频道传递
	 * @param i
	 */
	public void execute(int i);
}

 然后在Controller中添加channelUndo()方法,用于进行频道返回。并且需要进行一些简单的修改。

public class Controller {
	private Command openTVCommand;
	private Command closeTVCommand;
	private Command changeChannelCommand;
	
	public int nowChannel = 0;       //当前频道
	public int priorChannel;     //前一个频道,用于执行返回操作
	
	public Controller(Command openTvCommand,Command closeTvCommand,Command changeChannelCommand){
		this.openTVCommand = openTvCommand;
		this.closeTVCommand = closeTvCommand;
		this.changeChannelCommand = changeChannelCommand;
	}
	
	/**
	 * 打开电视剧
	 */
	public void open(){
		openTVCommand.execute(0);
	}
	
	/**
	 * 关闭电视机
	 */
	public void close(){
		closeTVCommand.execute(0);
	}
	
	/**
	 * 换频道:只在当前频道递增
	 */
	public void change(){
		priorChannel = nowChannel;            //换频道前记录当前频道
		nowChannel++;       //频道+1
		changeChannelCommand.execute(nowChannel);
	}
	
	/**
	 * 频道返回
	 */
	public void ChannelUndo(){
		changeChannelCommand.execute(priorChannel);          //将以前的频道传入
		//当前频道与前一个频道进行互换
		int tempChannel;
		tempChannel = priorChannel;
		priorChannel = nowChannel;
		nowChannel = tempChannel;
	}
}

客户端:

public class Client {
		public static void main(String a[])
		{
			Television tv = new Television();
                        Command openCommand,closeCommand,changeCommand;
			
			openCommand = new OpenTvCommand(tv);
			closeCommand = new CloseTvCommand(tv);
			changeCommand = new ChangeChannelCommand(tv);
			
			Controller control = new Controller(openCommand,closeCommand,changeCommand);
			
			control.open();           //打开电视机
			control.change();         //换频道
			control.change();
			control.ChannelUndo();
			control.ChannelUndo();
			control.ChannelUndo();
			control.close();          //关闭电视机
		}
}

运行结果:

Java设计模式之行为型:命令模式


设计模式系列文章:

Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

Java设计模式之创建型:建造者模式

Java设计模式之创建型:单例模式

Java设计模式之创建型:原型模式

Java设计模式之结构型:适配器模式

Java设计模式之结构型:装饰器模式

Java设计模式之结构型:代理模式

Java设计模式之结构型:桥接模式

Java设计模式之结构型:外观模式

Java设计模式之结构型:组合模式

Java设计模式之结构型:享元模式

Java设计模式之行为型:策略模式

Java设计模式之行为型:模板方法模式

Java设计模式之行为型:责任链模式

Java设计模式之行为型:观察者模式

Java设计模式之行为型:访问者模式

Java设计模式之行为型:中介者模式

Java设计模式之行为型:命令模式

Java设计模式之行为型:状态模式

Java设计模式之行为型:备忘录模式

Java设计模式之行为型:迭代器模式

Java设计模式之行为型:解释器模式


参考文章:设计模式读书笔记—–命令模式_chenssy 的技术博客-CSDN博客

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/114640.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号