请求发送者与接收者解耦——命令模式(二)

3 完整解决方案

       为了降低功能键与功能处理类之间的耦合度,让用户可以自定义每一个功能键的功能,Sunny软件公司开发人员使用命令模式来设计“自定义功能键”模块,其核心结构如图4所示:

 

自定义功能键核心结构图

       在图4中,FBSettingWindow是“功能键设置”界面类,FunctionButton充当请求调用者,Command充当抽象命令类,MinimizeCommandHelpCommand充当具体命令类,WindowHanlderHelpHandler充当请求接收者。完整代码如下所示:

[java]  view plain copy
  1. import java.util.*;  
  2.   
  3. //功能键设置窗口类  
  4. class FBSettingWindow {  
  5.     private String title; //窗口标题  
  6.     //定义一个ArrayList来存储所有功能键  
  7.     private ArrayList<FunctionButton> functionButtons = new ArrayList<FunctionButton>();  
  8.       
  9.     public FBSettingWindow(String title) {  
  10.         this.title = title;  
  11.     }  
  12.       
  13.     public void setTitle(String title) {  
  14.         this.title = title;  
  15.     }  
  16.       
  17.     public String getTitle() {  
  18.         return this.title;  
  19.     }  
  20.       
  21.     public void addFunctionButton(FunctionButton fb) {  
  22.         functionButtons.add(fb);  
  23.     }  
  24.       
  25.     public void removeFunctionButton(FunctionButton fb) {  
  26.         functionButtons.remove(fb);  
  27.     }  
  28.       
  29.     //显示窗口及功能键  
  30.     public void display() {  
  31.         System.out.println("显示窗口:" + this.title);  
  32.         System.out.println("显示功能键:");  
  33.         for (Object obj : functionButtons) {  
  34.             System.out.println(((FunctionButton)obj).getName());  
  35.         }  
  36.         System.out.println("------------------------------");  
  37.     }     
  38. }  
  39.   
  40. //功能键类:请求发送者  
  41. class FunctionButton {  
  42.     private String name; //功能键名称  
  43.     private Command command; //维持一个抽象命令对象的引用  
  44.       
  45.     public FunctionButton(String name) {  
  46.         this.name = name;  
  47.     }  
  48.       
  49.     public String getName() {  
  50.         return this.name;  
  51.     }  
  52.       
  53.     //为功能键注入命令  
  54.     public void setCommand(Command command) {  
  55.         this.command = command;  
  56.     }  
  57.       
  58.     //发送请求的方法  
  59.     public void onClick() {  
  60.         System.out.print("点击功能键:");  
  61.         command.execute();  
  62.     }  
  63. }  
  64.   
  65. //抽象命令类  
  66. abstract class Command {  
  67.     public abstract void execute();  
  68. }  
  69.   
  70. //帮助命令类:具体命令类  
  71. class HelpCommand extends Command {  
  72.     private HelpHandler hhObj; //维持对请求接收者的引用  
  73.       
  74.     public HelpCommand() {  
  75.         hhObj = new HelpHandler();  
  76.     }  
  77.       
  78.     //命令执行方法,将调用请求接收者的业务方法  
  79.     public void execute() {  
  80.         hhObj.display();  
  81.     }  
  82. }  
  83.   
  84. //最小化命令类:具体命令类  
  85. class MinimizeCommand extends Command {  
  86.     private WindowHanlder whObj; //维持对请求接收者的引用  
  87.       
  88.     public MinimizeCommand() {  
  89.         whObj = new WindowHanlder();  
  90.     }  
  91.       
  92. //命令执行方法,将调用请求接收者的业务方法  
  93.     public void execute() {  
  94.         whObj.minimize();  
  95.     }  
  96. }  
  97.   
  98. //窗口处理类:请求接收者  
  99. class WindowHanlder {  
  100.     public void minimize() {  
  101.         System.out.println("将窗口最小化至托盘!");  
  102.     }  
  103. }  
  104.   
  105. //帮助文档处理类:请求接收者  
  106. class HelpHandler {  
  107.     public void display() {  
  108.         System.out.println("显示帮助文档!");  
  109.     }  
  110. }  

       为了提高系统的灵活性和可扩展性,我们将具体命令类的类名存储在配置文件中,并通过工具类XMLUtil来读取配置文件并反射生成对象,XMLUtil类的代码如下所示:

[java]  view plain copy
  1. import javax.xml.parsers.*;  
  2. import org.w3c.dom.*;  
  3. import org.xml.sax.SAXException;  
  4. import java.io.*;  
  5.   
  6. public class XMLUtil {  
  7. //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象,可以通过参数的不同返回不同类名节点所对应的实例  
  8.     public static Object getBean(int i) {  
  9.         try {  
  10.             //创建文档对象  
  11.             DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();  
  12.             DocumentBuilder builder = dFactory.newDocumentBuilder();  
  13.             Document doc;                             
  14.             doc = builder.parse(new File("config.xml"));   
  15.           
  16.             //获取包含类名的文本节点  
  17.             NodeList nl = doc.getElementsByTagName("className");  
  18.             Node classNode = null;  
  19.             if (0 == i) {  
  20.                 classNode = nl.item(0).getFirstChild();  
  21.             }  
  22.             else {  
  23.                 classNode = nl.item(1).getFirstChild();  
  24.             }   
  25.   
  26.             String cName = classNode.getNodeValue();  
  27.               
  28.             //通过类名生成实例对象并将其返回  
  29.             Class c = Class.forName(cName);  
  30.             Object obj = c.newInstance();  
  31.             return obj;  
  32.         }     
  33.         catch(Exception e){  
  34.             e.printStackTrace();  
  35.             return null;  
  36.         }  
  37.     }  
  38. }  

       配置文件config.xml中存储了具体建造者类的类名,代码如下所示:

[java]  view plain copy
  1. <?xml version="1.0"?>  
  2. <config>  
  3.     <className>HelpCommand</className>  
  4.     <className>MinimizeCommand</className>  
  5. </config>  

       编写如下客户端测试代码:

[java]  view plain copy
  1. class Client {  
  2.     public static void main(String args[]) {  
  3.         FBSettingWindow fbsw = new FBSettingWindow("功能键设置");  
  4.               
  5.         FunctionButton fb1,fb2;  
  6.         fb1 = new FunctionButton("功能键1");  
  7.         fb2 = new FunctionButton("功能键1");  
  8.           
  9.         Command command1,command2;  
  10.         //通过读取配置文件和反射生成具体命令对象  
  11.         command1 = (Command)XMLUtil.getBean(0);  
  12.         command2 = (Command)XMLUtil.getBean(1);  
  13.           
  14.         //将命令对象注入功能键  
  15.         fb1.setCommand(command1);  
  16.         fb2.setCommand(command2);  
  17.           
  18.         fbsw.addFunctionButton(fb1);  
  19.         fbsw.addFunctionButton(fb2);  
  20.         fbsw.display();  
  21.           
  22.         //调用功能键的业务方法  
  23.         fb1.onClick();  
  24.         fb2.onClick();  
  25.     }  
  26. }  

       编译并运行程序,输出结果如下:

显示窗口:功能键设置

显示功能键:

功能键1

功能键1

------------------------------

点击功能键:显示帮助文档!

点击功能键:将窗口最小化至托盘!

       如果需要修改功能键的功能,例如某个功能键可以实现“自动截屏”,只需要对应增加一个新的具体命令类,在该命令类与屏幕处理者(ScreenHandler)之间创建一个关联关系,然后将该具体命令类的对象通过配置文件注入到某个功能键即可,原有代码无须修改,符合“开闭原则”。在此过程中,每一个具体命令类对应一个请求的处理者(接收者),通过向请求发送者注入不同的具体命令对象可以使得相同的发送者对应不同的接收者,从而实现“将一个请求封装为一个对象,用不同的请求对客户进行参数化”,客户端只需要将具体命令对象作为参数注入请求发送者,无须直接操作请求的接收者。

 

【作者:刘伟   http://blog.csdn.net/lovelion

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
命令模式是一种行为型设计模式,用于将请求封装成一个对象,从而使得请求发送者接收者解耦。它将请求的操作封装在一个命令对象中,使得可以将不同的请求参数化,并且支持命令的撤销、恢复等操作。 命令模式的关键角色包括命令接口、具体命令命令发送者命令接收者命令接口定义了执行命令的方法,具体命令实现了具体的命令逻辑,命令发送者负责创建和发送命令对象,命令接收者负责执行具体的命令操作。 命令模式的优点包括: 1. 解耦请求发送者接收者:通过将请求封装在命令对象中,使得请求发送者接收者解耦,它们之间不需要直接通信。 2. 支持命令的撤销、恢复:由于命令对象封装了命令的操作,可以方便地实现命令的撤销、恢复等功能。 3. 支持扩展新的命令:通过新增具体命令类,可以方便地扩展新的命令,而无需修改现有的代码。 然而,命令模式也存在一些缺点: 1. 增加了类的数量:引入命令对象会增加额外的类,增加了代码的复杂性。 2. 可能导致系统过于臃肿:如果命令对象过多,可能会导致系统的类数量过多,增加维护和理解的难度。 总的来说,命令模式适用于需要将请求发送者接收者解耦的情况下,能够提供一种统一的命令接口,使得请求发送者不需要关心具体的命令逻辑。在需要支持命令的撤销、恢复等功能,或者需要扩展新的命令的场景下,命令模式一个很好的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值