设计模式的艺术 行为型模式之命令模式
前言
装修新房子的最后几道工序之一是安装插座和开关,通过开关可以控制一些电器的打开和关闭,例如电灯或者排气扇,但是购买开关时其实并不知道它能够控制什么具体的电器。在软件设计中,也存在着这样的关系,请求发送者和接收者对象,为了降低之间的耦合度,我们会将两者进行解耦,会在两者之间引入了新的命令对象,我们将它成为命令模式
什么是命令模式 Command Pattern
将一个请求封装为一个对象,从而可用不同的请求对客户端进行参数化;对请求排队或者记录请求日志,以及支持可撤销操作。命令模式是一种对象行为型模式,其别名为动作模式或者事务模式
命名模式的优点
(1)、降低了系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者直接实现了完全的解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
(2)、新的命令可以很容易的加入到系统当中,由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码甚至客户类代码,满足开闭原则的要求
(3)、可以比较容易的设计一个命令队列或宏命令(组合命令)
(4)、为请求的撤销和恢复操作提供了一种设计和实现的方案
命令模式的缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要设计一个具体的命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。
命令模式的使用场景
(1)、系统中需要将请求调用者和请求接收者进行解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,无须知道接收者是谁,接收者也无须知道何时被调用。
(2)、系统需要在不同的时间指定请求,将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身依旧是活动的,可以通过该命令对象去调用请求接收者,无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
(3)、系统需要支持命令的撤销和恢复操作
(4)、系统需要将一组操作组合在一起形成宏命令。
命令模式的具体实现
目录结构
请求接收者类
//帮助文档处理类:请求接收者 public class HelpHandler { public void display(){ System.out.println("显示帮助文档!"); } }
抽象命令类
package com.company.command; //抽象命令类 public abstract class Command { public abstract void execute(); }
具体命令类
import com.company.handler.HelpHandler; public class HelpCommand extends Command { private HelpHandler hhobj; //维持对请求接收者的引用 public HelpCommand() { this.hhobj = new HelpHandler(); } @Override //命令执行方法,将调用请求接收者的业务方法 public void execute() { hhobj.display(); } }
//最小化命令类:具体命令类 public class MinimizeCommand extends Command { private WindowHandler whobj; //维持对请求接收者的引用 public MinimizeCommand(){ whobj=new WindowHandler(); } @Override //命令执行方法,将调用请求接收者的业务方法 public void execute() { whobj.minimize(); } }
功能键类
//功能键类:请求发送者 public class FunctionButton { private String name; //功能键名称 private Command command; //维持一个抽象命令对象的引用 public String getName() { return name; } public FunctionButton(String name) { this.name = name; } //为功能键注入命令 public void setCommand(Command command) { this.command = command; } //发送请求的方法 public void onClick(){ System.out.println("点击功能键"); command.execute(); } }
功能键设置窗口类
//功能键设置窗口类 public class FBSettingWindow { private String title; // 窗口标题 //定义一个ArrayList来存储所有功能键 private ArrayList<FunctionButton> functionButtons=new ArrayList<FunctionButton>(); public FBSettingWindow(String title) { this.title = title; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public void addFunctionButton(FunctionButton fb){ functionButtons.add(fb); } public void removeFunctionButton(FunctionButton fb){ functionButtons.remove(fb); } //显示窗口及功能 public void dispay(){ System.out.println("显示窗口:"+this.title); System.out.println("显示功能键:"); for(Object obj:functionButtons){ System.out.println(((FunctionButton)obj).getName()); } System.out.println("-----------------------------"); } }
辅助功能类
//辅助工具类 public class XMLUtil { public static Object getBean(int i){ try { //创建文档的对象 DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); DocumentBuilder builder=factory.newDocumentBuilder(); Document document=builder.parse(new File(XMLUtil.class.getClassLoader().getResource("").getPath()+"config.xml")); //获取包含类名的文本节点 NodeList nodeList=document.getElementsByTagName("className"); Node node=null; if(0==i){ node=nodeList.item(0).getFirstChild(); }else{ node=nodeList.item(1).getFirstChild(); } String cName=node.getNodeValue(); //通过类名生成实例对象并将其返回 Class c=Class.forName(cName); Object object=c.newInstance(); return object; }catch (Exception e){ e.printStackTrace(); return null; } } }
客户端测试类
public class Client { public static void main(String[] args) { FBSettingWindow fbSettingWindow=new FBSettingWindow("功能键设置"); FunctionButton fb1,fb2; fb1=new FunctionButton("功能键1"); fb1=new FunctionButton("功能键1"); fb2=new FunctionButton("功能键2"); Command command1,command2; //通过配置文件和反射生成具体命令对象 command1=(Command)XMLUtil.getBean(0); command2=(Command)XMLUtil.getBean(1); //将命令对象注入功能键 fb1.setCommand(command1); fb2.setCommand(command2); fbSettingWindow.addFunctionButton(fb1); fbSettingWindow.addFunctionButton(fb2); fbSettingWindow.dispay(); //调用功能键的业务方法 fb1.onClick(); fb2.onClick(); } }