我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现命令模式(设计模式)。
不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。
我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个命令模式,真实的实现一个,你看懂代码后,自然就可以明白了。
命令模式Command Pattern(行为型设计模式)
定义:将请求发送者与请求接收者解耦,请求发送者通过命令对象来间接引用接收者,使得系统 具有更好的灵活性,可以在不修改现有系统代码的情况下,让相同的发送者对应不同的接收者。
上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。
不知道大家有没有这样的想法,当我们打开Windows系统,打开一个对话框,右上角是不是有一个关闭按钮?那么这个按钮就是一个“关闭窗口”请求的发送者。而按钮单击事件处理类则是该请求的接收者。Windows系统中大量使用这种技术来实现GUI图形界面。那这是怎么实现的呢?下面我们就来用JAVA实现一个。
为了降低系统的耦合度,将请求发送者与请求接收者解耦,可以使用命令模式来设计系统。在发送者与接收者之间引入了新的命令对象,将发送者的请求封装在命令对象中,再通过命令对象来调用接收者的方法。
命令模式的核心在于引入了命令类,通过命令类来降低发送者和接收者的耦合度,请求发送者只需指定一个命令对象,再通过命令对象来调用请求接收者的处理方法,其结构如图3所示:
在命令模式结构图中包含Command(抽象命令类)、ConcreteCommand(具体命令类)、Invoker(调用者)、 Receiver(接收者)等4个角色。角色你可以理解成scratch编程中的角色,哈哈。抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。接收者执行与请求相关的操作,它具体实现对请求的业务处理。
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。
命令模式的关键在于引入了抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。在最简单的抽象命令类中只包含了一个抽象的execute()方法,每个具体命令类将一个Receiver类型的对象作为一个实例变量进行存储,从而具体指定一个请求的接收者,不同的具体命令类提供了execute()方法的不同实现,并调用不同接收者的请求处理方法。
JAVA代码实现
public abstract class Command {
public abstract void execute();
}
public class Invoker {
private Command command;
public Invoker(Command command) {
this.command=command;
}
public void setCommand(Command command) {
this.command=command;
}
public void call() {
command.execute();
}
}
public class ConcreteCommand extends Command {
private Receiver receiver;
public void execute() {
receiver.action();
}
}
public class Receiver {
public void action() {
//具体操作
}
}
应用实例
我们大家在打游戏时,是不是在打之前都要去设置热键或功能键?比如有的人会把H设成换武器,有的人会把H设置成放绝招,这个看你喜好。那这个功能是怎么实现的呢,如何方便玩家设置功能键呢,就是用了命令模式。我们往下看,我们通过热键来设置用一个键,有时可以控制退出,有时可以控制提示帮助框显示。
1.功能键类,请求发送者
package designpatterns.command;
public class FunctionButton {
private Command command;
public void setCommand(Command command) {
this.command=command;
}
public void click() {
System.out print("单击该键:");
command.execute();
}
}
2.抽象命令类
package designpatterns.command;
public abstract class Command {
public abstract void execute();
}
3.退出命令类
package designpatterns.command;
public class ExitCommand extends Command {
private SystemExitClass seObj;
public ExitCommand() {
seObj=new SystemExitClass();
}
public void execute() {
seObj.exit();
}
}
4.帮助命令类
package designpatterns.command;
public class HelpCommand extends Command {
private DisplayHelpClass hcObj;
public HelpCommand() {
hcObj=new DisplayHelpClass();
}
public void execute() {
hcObj.display();
}
}
5.退出系统类,请求接收者
package designpatterns.command;
public class SystemExitClass {
public void exit() {
System.out.println("退出");
}
}
6.显示帮助文档类,请求接收者
package designpatterns.command;
public class DisplayHelpClass {
public void display() {
System.out.println("显示帮助");
}
}
7.配置文件config.xml
<?xml version=1.0"?>
<config>
<className>designpatterns.command.ExitCommand</className>
</config>
8.工具类
package designpatterns.command;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
public class XMLUtil {
public static Object getBean() {
try{
DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder=dFactory.newDocumentBuilder();
Document doc;
doc=builder.parse(new File("src//designpatterns//command//config.xml"));
NodeList nl=doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
9.客户端测试类
package designpatterns.command;
public class Client {
public static void main(String args[]) {
FunctionButton fb=new FunctionButton();
Command command;
command=(Command)XMLUtil.getBean();
fb.setCommand(command);
fb.click();
}
}
这个程序编译并运行后,输出结果是:
单击该键:退出
如果你要换成用这个键去产生帮助信息的效果,那你只需要修改XML配置文件就可以了,而不需要改代码。所以这个设计模式的真正用意就是帮程序员省时省力,就是这个目的。用本人自创的画图法可以认为以上类有如下执行关系。
总结
你可能不知道的是,在Windows系统环境下,由于大量使用GUI,所以在基于GUI的软件开发中,无论是电脑桌面应用还是手机移动应用,命令模式都得到了广泛的应用。要学好Windows系统的各类编程,你必须先搞懂命令模式。
当然,有时会遇到有不止一个请求接收者的情况,那就要用命令队列来实现了,Invoker原本里面只有一个command,现在要改成一个commandQueue了。
另外,命令模式还可以实现Windows系统中的Windows系统日志功能、Word里的撤销操作以及宏命令等。可以说,命令模式在Windows系统中是一个运用非常广泛的设计模式,学习各类面向对象编程的同学一定要牢牢掌握。
各位小伙伴,这次我们就说到这里,下次我们再深入研究windows环境下的设计模式,相信你一定能喜欢上windows。如果要转载我的文章请说明出处哦。
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。