命令模式(Command Pattern)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
描述:
通常,面向对象应用程序是由一组能够提供有限的、专注于功能的相互交互的对象集合组成。为了响应用户的交互动作,应用程序执行一系列的处理。为了响应用户的请求,应用程序使用不同的对象提供的服务处理请求。根据实现,应用可以指定一个对象,这个指定的对象可以调用不同对象上的操作,被称为调用者(invoker)。(译者注:其实个人认为这个invoker在这里主动地成分更大,它是偏向于调用其他对象,这个区别就像英语employ这个动词是雇佣的意思,employee是员工??被雇佣的对象,而employer是雇主??雇佣的主动者,那么在这里的invoker是一个道理)。调用者(invoker)可以看作为客户应用程序的一部分。那些包含提供处理请求服务实现的对象集合成为接受对象(Receiver Object)。
Figure 30.1: Object Interaction?Before Applying the Command Pa ttern
在这种设计中,发出请求的应用程序和提供处理请求服务的接受对象(Receiver Object)集合之间彼此是紧密关联的,因为它们之间直接相互交互。这导致了在调用者(invoker)实现中会包含大量的if条件语句。
1.…
2.if(RequestType=TypeA){
3.//do something
4.}
5.
6.if(RequestType=TypeB){
7.//do something
8.}
9.…
当一种新的处理类型需要加入的应用时,现存的代码需要进行修改??这违背了面向对象开放?关闭的基本原则。
1.…
2.if(RequestType=TypeA){
3.//do something
4.}
5.…
6.if(RequestType=NewType){
7.//do something
8.}
9.…
使用命令模式,代表客户发布请求的调用者(invoker)和服务的处理者??接受对象(Receiver Object)之间不具有耦合性。命令模式推荐创建一个可以为响应客户请求而执行处理或者采取动作的抽象。指定这个抽象声明一个被其它不同的具体的实现者所实现的共同接口,这些具体的实现者称之为命令对象(Command Objects)。每一个命令对象(Command Object)代表一种不同的客户请求和对其进行的相应处理。在图30.2中,command接口代表了这个抽象。它声明一个excute方法,它由两个具体的实现者(类)??ConcreteCommand_1和ConcreteCommand_2。
Figure 30.2: Command Object Hierarchy
一个给定的命令对象(command Object)负责提供处理它所代表的请求的功能,但是命令对象并不包含此功能的真实实现,它是通过使用接受对象
(Receiver Object)来提供处理功能。(如图30.3)
Figure 30.3: Class Association??After the Command Pattern Is Applied
当客户应用程序响应用户(或者其他应用)的交互而需要提供服务时:
(1)客户创建需要的接受对象(Receiver Object)。
(2)客户创建一个相应的命令对象(Command Object),并用在步骤1中创建的接受对象(Receiver Object)配置这个命令对象。
(3)客户创建一个调用者的实例(invoker), 并用在步骤2中创建的命令对象(Command Object)配置调用者。
(4)调用者调用命令对象(Command Object)上的execute方法。(5)作为execute方法实现的一部分,特定的命令对象
(Command Object)调用它所包含的接受对象上的必要方法来提供需要的服务。在新的设计中:
(1)客户/调用者(invoker)不直接与接受对象(Receiver)进行交互,它们之间完全不耦合。
(2)当应用程序需要提供新的类型的时候,可以增加一个新的命令(Command)对象。这不需要修改调用者(invoker)的代码。因此新的设计保证了面向对象的开放?封闭的原则。
(3)因为一个请求被指定为对象的形式,那么就存在以下可能性:1)把命令对象保存到持久介质上。
以后进行执行。
应用反向处理,实现恢复特性。
2)把不同的命令对象分组成一个独立的单元。
以下是一个FTP(File Transfer Protocol)客户事例应用程序,对于在实际应用程序中如何应用命令模式提供了很好的参考。
例子1:
让我们建立一个应用程序来模拟FTP客户端的工作。在Java 中,简单的FTP客户用户接口可以使用如下组件设计。
(1)两个JList对象用于本地和远程文件系统的显示。
(2)四个JButton对象用于初始不同的请求类型:上传、下载、删除和退出。
用户接口组件摆列在一个frame中,如图30.4
Figure 30.4: Simple FTP Client UI Display
每一个JButton对象创建的时候,实现了ActionListener接口的
Button-Handler类的一个事例是一个ActionListener的集合。
1.public class FTPGUI extends JFrame{
2.…
3.…
4.//Create buttons
5.btnUpload = new JButton(FTPGUI.UPLOAD);
6.btnUpload.setMnemonic(KeyEvent.VK_U);
7.…
8.…
9.ButtonHandler vf = new ButtonHandler();
10. btnUpload.addActionListener(vf);
11. btnDownload.addActionListener(vf);
12. btnDelete.addActionListener(vf);
13. btnExit.addActionListener(vf);
14. …
15. …
16.}//end of class
因为这个相同的ButtonHandler实例是所有JButton对象的
Action-Listener的一个集合,actionPerformed方法被所有的JButton对象调用。因此,ButtonHandler对象必须检测哪一个按钮被按下,并执行相应的处理。从Listing30.1中可以看到,actionPerformed方法的代码因为条件语句很不规则。当有很多按钮和菜单对象需要添加到FTP用户界面时,actionPerformed方法的代码就很混乱。同时,一个新的按钮对象被添加时,现在的actionPerformed 方法的代码必须修改。这违反了面向对象的开放??关闭原则。
Listing 30.1: ButtonHandler Class
1.class ButtonHandler implements ActionListener{
2.public void actionPerformed(ActionEvent e) {
3.//if statements - for different types of client r
equests
4.if(e.getActionCommand().equals(FTPGUI.EXIT)) {
5.System.exit(1);
6.}
7.if(e.getActionCommand().equals(FTPGUI.UPLOAD)) {
8.int index = localList.getSelectedIndex();
9.String selectedItem =
10. localList.getSelectedValue().toString();
11. ((DefaultListModel) localList.getModel()).remove(
12. index);
13. ((DefaultListModel) remoteList.getModel()).
14. addElement(selectedItem);
15. }
16. if(e.getActionCommand().equals(FTPGUI.DOWNLOAD)) {
17. int index = remoteList.getSelectedIndex();