返回顶部
首页 > 资讯 > 精选 >Java Tip: 实现Command模式 (转)
  • 365
分享到

Java Tip: 实现Command模式 (转)

2023-06-03 03:06:53 365人浏览 薄情痞子
摘要

Java Tip: 实现Command模式 (转)[@more@]Java Tip: 实现Command模式概述有时需要向对象发送请求,但是不知道 "被请求的操作" 或 "请求的接受者" 的任何信息。在面向过程的程序设计语言中,这类通信是通

Java Tip: 实现Command模式 (转)[@more@]

Java Tip: 实现Command模式

概述
有时需要向对象发送请求,但是不知道 "被请求的操作" 或 "请求的接受者" 的任何信息。在面向过程的程序设计语言中,这类通信是通过回调函数来完成的:在某个地方登记这个函数,然后在后面调用它。在面向对象程序中,command(命令)与回调函数等价,它封装了回调函数。本文演示如何在Java中实现Command模式。

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

设计模式不但可以加速面向对象工程的设计进度,而且可以提高开发小组的产出以及软件的质量。Commad模式是一种对象行为模式,它可以对发送者(sender)和接收者(receiver)完全解耦(decoupling)。("发送者" 是请求操作的对象,"接收者" 是接收请求并执行某操作的对象。有了 "解耦",发送者对接收者的接口一无所知。)这里,"请求"(request)这个术语指的是要被执行的命令。Command模式还让我们可以对 "何时" 以及 "如何" 完成请求进行改变。因此,Command模式为我们提供了灵活性和可扩展性。

在象C这样的程序设计语言中,函数指针常被用来消除庞大的switch语句。(参见 "Java Tip 30: Polymorphism and Java" 获得更详细的介绍)Java没有函数指针,所以我们可以用Command模式来实现回调函数。在下面的第一个代码示例TestCommand.java中,你将实际看到它是如何实现的。

一些开发者在其它语言中已经习惯于使用函数指针,因而他们往往禁不起诱惑,想以同样的方法使用Reflection api的Method对象。例如,在 "Java Reflection" 一文中,Paul Tremblett介绍了如何使用Reflection而不是switch语句来实现事务处理(Transaction) (参见 "相关资源" 获得Tremblett的文章和Sun的Reflection教程网址的链接)。我是不会为这样的诱惑所动的。正如Sun所指出的:在有其它更贴近于Java程序设计语言的工具满足使用要求的情况下,一般不提倡使用Reflect API。不使用Method对象,程序会更易于调试和维护。所以,你应该定义一个接口,并在类中实现它,以执行所需操作。

因此我建议,可以使用Command模式并结合Java的动态加载和绑定机制来实现函数指针。(关于Java的动态加载和绑定机制的详细介绍,参见Gosling和Henry McGilton的"The Java Language Environment -- A White Paper",列于"相关资源"。)

遵循上面的建议,我们可以运用Command模式,利用它所提供的多态性来消除庞大的switch语句,从而设计出可扩展的系统。我们还可以利用Java独有的动态加载和绑定机制来构筑动态的、并且可以动态扩展的系统。这一点在下面第二个代码示例TestTransactionCommand.java中进行说明。

Command模式使请求本身成为一个对象。这个对象和其它对象一样可以被存储和四处传递。这种模式的关键在于一个Command接口:它声明了一个接口,用于执行操作。最简单的形式下,这个接口包含一个抽象的execute操作。每个具体的Command类把接收者作为一个实例变量进行存储,从而指定了一对 "接收者" 和 "行为"。它为execute()方法提供不同的实现以进行请求调用。接收者知道如何执行请求。

如下的图1表示了Switch--一个Command对象的集合体(aggregation)。它的接口中有flipUp()和flipDown()两种操作。Switch被称为 "调用者"(invoker),因为它调用command接口中的execute操作。

具体的command,如LightOnCommand,实现command接口的execute操作。它知道去调用合适的接收者对象的操作。这种情况下它充当了一个适配器(adapter)。通过 "适配器"这一术语, 我想说明:具体的Command对象是一个简单的连接器,它连接具有不同接口的 "调用者" 和 "接收者"。

客户实例化调用者,接收者以及具体的command对象。

图2为时序图,它表示对象间的相互作用。它说明了Command如何对调用者和接收者(以及它执行的请求)解耦。客户用合适的接收者作为构造函数的参数来创建具体的command。然后,它将Command保存在调用者中。调用者回调具体的command,后者知道如何完成想要的Action()操作。

客户(代码中的主程序)创建具体的Command对象并设置它的接收者。作为一个调用者对象,Switch保存具体的Command对象。调用者通过对Command对象调用execute来发送请求。具体的Command对象对它的接收者进行操作调用,从而完成操作请求。

这里最关键的思想在于,具体的command用调用者来注册自身,调用者进行回调,在接收者身上执行命令。

Command模式示例代码
让我们来看一个简单的例子,它通过Command模式实现回调机制。

以Fan(风扇)和Light(灯)为例。我们的目标是设计一个Switch,它可以对任一个对象进行 "开" 和 "关" 的操作。Fan和Light具有不同的接口,这意味着Switch必须独立于接收者接口,或者说,它不清楚接收者的接口。为了解决这一问题,每个Switch都需要合适的command作为参数。很明显,连接到Light的Switch和连接到Fan的Switch具有不同的command。因此,Command类必须是抽象的,或者是个接口。

Switch的构造函数被调用时,它以一组合适的command作为参数。command作为Switch的私有变量保存下来。

调用flipUp()和flipDown()操作时,它们只是简单地让合适的command进行execute()操作。Switch对调用execute()后将发生些什么一无所知。

TestCommand.java
class Fan {
  public void startRotate() {
  System.out.println("Fan is rotating");
  }
  public void stopRotate() {
  System.out.println("Fan is not rotating");
  }
}
class Light {
  public void turnOn( ) {
  System.out.println("Light is on ");
  }
  public void turnOff( ) {
  System.out.println("Light is off");
  }
}
class Switch {
  private Command UpCommand, DownCommand;
  public Switch( Command Up, Command Down) {
  UpCommand = Up; // concrete Command reGISters itself with the invoker
  DownCommand = Down;
  }
  void flipUp( ) { // invoker calls back concrete Command, which executes the Command on the receiver
  UpCommand . execute ( ) ;


  }
  void flipDown( ) {
  DownCommand . execute ( );
  }
}
class LightOnCommand implements Command {
  private Light myLight;
  public LightOnCommand ( Light L) {
  myLight  =  L;
  }
  public void execute( ) {
  myLight . turnOn( );
  }
}
class LightOffCommand implements Command {
  private Light myLight;
  public LightOffCommand ( Light L) {
  myLight  =  L;
  }
  public void execute( ) {
  myLight . turnOff( );
  }
}
class FanOnCommand implements Command {
  private Fan myFan;
  public FanOnCommand ( Fan F) {
  myFan  =  F;
  }
  public void execute( ) {
  myFan . startRotate( );
  }
}
class FanOffCommand implements Command {
  private Fan myFan;

  public FanOffCommand ( Fan F) {
  myFan  =  F;
  }
  public void execute( ) {
  myFan . stopRotate( );
  }
}
public class TestCommand {
  public static void main(String[] args) {
  Light  testLight = new Light( );
  LightOnCommand testLOC = new LightOnCommand(testLight);
  LightOffCommand testLFC = new LightOffCommand(testLight);
  Switch testSwitch = new Switch(testLOC,testLFC); 
  testSwitch.flipUp( );
  testSwitch.flipDown( );
  Fan testFan = new Fan( );
  FanOnCommand foc = new FanOnCommand(testFan);
  FanOffCommand ffc = new FanOffCommand(testFan);
  Switch ts = new Switch( foc,ffc);
  ts.flipUp( );
  ts.flipDown( );
  }

Command.java
public interface Command {
  public abstract void execute ( );
}

在上面的示例代码中,Command模式将 "调用操作的对象" (Switch)和 "知道如何执行操作的对象" (Light和Fan)完全分离开来。这带来了很大的灵活性:发送请求的对象只需要知道如何发送;它不必知道如何完成请求。

Command模式实现Transaction
Command模式也被称为action(动作)模式或transaction(事务)模式。假设有一个服务器,它接收并处理客户通过tcp/IP Socket连接发送的transaction。这些transaction包含一个命令,后跟零个或多个 参数。

一些设计者可能会使用switch语句,每个command对应一个case。在一个面向对象工程的设计中,代码中如果使用switch语句,往往表示这是一个糟糕的设计。Command模式展现的是支持transaction的面向对象的方法,它可以用于解决这类设计问题。

在TestTransactionCommand.java程序的客户代码中,所有请求都被封装在通用的TransactionCommand对象中。TransactionCommand对象由客户创建并用CommandManager进行登记。等待的请求可以通过调用runCommands()在不同时期被执行,这带来了很大的灵活性。而且我们还可以将多个command组装成一个复合command。示例代码中还有CommandArgument,CommandReceiver,CommandManager这些类,以及TransactionCommand的子类--AddCommand和SubtractCommand。下面是对这些类的介绍。

· CommandArgument是一个helper类,它保存命令的参数。如果是大量(或可变数量)的任何类型的参数,它可以被重写,以简化参数的传递工作。

· CommandReceiver实现所有的命令处理方法(command-processing method),它用Singleton模式来实现。

· CommandManager是调用者,和前面例子中的Switch相当。它在其私有myCommand变量中保存通用TransactionCommand对象。runCommands()被调用时,它调用合适的TransactionCommand对象的execute()。

Java中,可以根据一个包含类名的字符串查找类的定义。在TransactionCommand类的execute ()操作中,我先计算出类名,然后将它链接到运行系统中--也就是说,类是根据需要被即时载入的。这里所采用的命名方式是,在命令名后连接一个 "Command" 字符串作为transaction command子类名,这样它就可以被动态载入。

注意,newInstance()返回的Class对象必须被转换为合适的类型。这意味着新的类要么必须实现一个接口,要么继承一个在编译期就为程序所知道的现有的类。本例中我们是实现Command接口,所以不存在问题。
file://TestTransactionCommand.java
import java.util.*;

final class CommandReceiver {
  private int[] c;

  private CommandArgument a;

  private CommandReceiver(){

  c = new int[2];

  }
  private static CommandReceiver cr = new CommandReceiver();
  public static CommandReceiver getHandle() {
return cr;

  }
  public void setCommandArgument(CommandArgument a) {
this.a = a;

  }
  public void methAdd() {
c = a.getArguments();

  System.out.println("The result is " + (c[0]+c[1]));

  }
  public void methSubtract() {
c = a.getArguments();

  System.out.println("The result is " + (c[0]-c[1]));

  }
}
class CommandManager {
  private Command myCommand;
  public CommandManager(Command  myCommand) {

  this.myCommand  =  myCommand ; 

  }
  public void runCommands( ) {

  myCommand.execute(); 

  }
}

class TransactionCommand implements Command {
  private CommandReceiver commandreceiver;

  private Vector commandnamelist,commandargumentlist;

  private String commandname;

  private CommandArgument commandargument;

  private Command command;
  public TransactionCommand () {
this(null,null);

  }
  public TransactionCommand ( Vector  commandnamelist, Vector
commandargumentlist){

  this.commandnamelist = commandnamelist;

  this.commandargumentlist = commandargumentlist;

  commandreceiver =  CommandReceiver.getHandle(); 

  }
  public void execute( ) {
  for (int i = 0; i < commandnamelist.size(); i++) {
  commandname = (String)(commandnamelist.get(i));

  commandargument = (CommandArgument)((commandargumentlist.get(i)));

  commandreceiver.setCommandArgument(commandargument);

  String classname = commandname + "Command";
  try {

  Class cls = Class.forName(classname);

  command = (Command) cls.newInstance();

  }

  catch (Throwable e) { 

  System.err.println(e);

  }

  command.execute();

  }

  }
}
class AddCommand extends TransactionCommand {

  private CommandReceiver cr;
  public AddCommand () {

  cr = CommandReceiver.getHandle(); 

  } 
  public void execute( ) { 

  cr.methAdd(); 

  } 

}
class SubtractCommand extends TransactionCommand {

  private CommandReceiver cr;
  public SubtractCommand () {

  cr = CommandReceiver.getHandle(); 

  }
  public void execute( ) {

  cr.methSubtract();

  } 

}

class CommandArgument {

  private int[] args;
  CommandArgument() {

  args = new int[2];

  }

  public int[] getArguments() {
return args;

  }

  public void setArgument(int i1, int i2) {

  args[0] = i1; args[1] = i2;

  }

}
public class TestTransactionCommand {

  private  Vector clist,alist;
  public TestTransactionCommand() {
clist = new Vector();

  alist = new Vector();

  }
  public void clearBuffer(Vector c, Vector a) {
clist.removeAll(c);

  alist.removeAll(a);

  }
  public Vector getClist() {
return clist;

  }
  public Vector getAlist() {
return alist;

  }
  public static void main(String[] args) {

  CommandArgument ca,ca2;
  TestTransactionCommand t = new TestTransactionCommand();
  ca = new CommandArgument();

  ca.setArgument(2,8);

  Vector myclist = t.getClist();

  Vector myalist = t.getAlist();

  myclist.addElement("Add"); myalist.addElement(ca);
  TransactionCommand tc = new TransactionCommand(myclist,myalist);

  CommandManager cm = new CommandManager(tc); 

  cm.runCommands();
  t.clearBuffer(myclist,myalist);

  ca2 = new CommandArgument();

  ca2.setArgument(5,7);

  myclist = t.getClist();

  myalist = t.getAlist();

  myclist.addElement("Subtract"); myalist.addElement(ca2);

  myclist.addElement("Add"); myalist.addElement(ca2);
  TransactionCommand tc2 = new TransactionCommand(myclist,myalist); 

  CommandManager cm2 = new CommandManager(tc2); 

  cm2.runCommands();
  }

命令及其参数保存在列表中,并被封装成通用TransactionCommand对象。通用TransactionCommand用CommandManager来注册。任何时候,命令可以在CommandManager类中通过调用runCommands()接口来执行。

客户代码不依赖于任何具体的TransactionCommand子类,也就是说,我的设计是针对接口而不是实现。这带来了灵活性:要想增加一个新的命令,只需要定义一个新的TransactionCommand子类,并在CommandReceiver类中提供新的命令处理方法的实现。仅此而已。

结论
Command模式具有以下优点:

1. command将 "进行操作请求" 的对象和 "知道如何执行操作" 的对象分离开来(即,解耦)。

2. command是个很棒的对象。它可以象任何其它对象一样被使用和继承。

3. 多个command可以被组装成一个复合command。

4. 很容易增加新的command,因为不需要修改现有的类。

如果执行过的命令序列被保存在一个历史列表中,就可以遍历这个列表来提供undo和redo操作。要想实现这一功能,必须在Command接口中有一个unexecute()操作。这一练习留给读者自己去完成。

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

相关资源
· Design Patterns by Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1994, ISBN 0-201-63361-2 Http://www.bookbuyer.com/cgi-bin/getTitle.cgi?ISBN=0201633612

· Dr. Dobb's Journal, January 1998: "Java Reflection: Not just for tool developers," by Paul Tremblett http://www.ddj.com/articles/1998/9801/9801c/9801c.htm

· Sun's Reflection page
http://java.sun.com/docs/books/tutorial/reflect/index.html

· "The Java Language Environment -- A White Paper" (May 1996), by James Gosling and Henry McGilton covers details about Java's dynamic loading and binding mechanism
http://java.sun.com/docs/white/langenv/
For more on taking advantage of Java's unique feature of dynamic loading and binding mechanism to build a dynamic and dynamically-extensible system, see
http://java.sun.com/docs/white/langenv/


--结束END--

本文标题: Java Tip: 实现Command模式 (转)

本文链接: https://lsjlt.com/news/232535.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java Tip: 实现Command模式 (转)
    Java Tip: 实现Command模式 (转)[@more@]Java Tip: 实现Command模式概述有时需要向对象发送请求,但是不知道 "被请求的操作" 或 "请求的接受者" 的任何信息。在面向过程的程序设计语言中,这类通信是通...
    99+
    2023-06-03
  • C#中通过Command模式实现Redo/Undo方案
    一个比较常见的改进用户体验的方案是用Redo/Undo来取代确认对话框,由于这个功能比较常用,本文简单的给了一个在C#中通过Command模式实现Redo/Undo方案的例子,以供后...
    99+
    2024-04-02
  • Java设计模式之桥接模式的实现
    桥接模式 桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。 ...
    99+
    2024-04-02
  • Java实现设计模式之责任链模式
    目录1.什么是责任链模式2.如何实现3.代码实现4.总结1.什么是责任链模式 当一个请求可能需要多个对象中的某个进行处理时,将这些对象连成一条链,并沿者这条链传递该请求,知道有一个对...
    99+
    2024-04-02
  • Java实现对象转CSV格式
    目录介绍代码样例测试样例介绍 csv全称“Comma-Separated Values”,是一种逗号分隔值格式的文件,是一种用来存储数据的纯文本格式文件。CS...
    99+
    2024-04-02
  • Java实现CSV格式转对象
    目录介紹参考代码测试用例介紹 csv全称“Comma-Separated Values”,是一种逗号分隔值格式的文件,是一种用来存储数据的纯文本格式文件。CS...
    99+
    2024-04-02
  • Java设计模式之如何实现桥接模式
    这篇文章主要为大家展示了“Java设计模式之如何实现桥接模式”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java设计模式之如何实现桥接模式”这篇文章吧。桥接模式桥接模式是将抽象部分与它的实现部...
    99+
    2023-06-15
  • Java设计模式的工厂模式怎么实现
    本篇内容介绍了“Java设计模式的工厂模式怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!工厂模式在Java应用程序中对象无处不在,这...
    99+
    2023-06-29
  • Java设计模式的单例模式如何实现
    这篇文章主要介绍了Java设计模式的单例模式如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java设计模式的单例模式如何实现文章都会有所收获,下面我们一起来看看吧。单例模式单例模式顾名思义就是单一的实例...
    99+
    2023-06-29
  • Java设计模式的桥接模式怎么实现
    这篇文章主要讲解了“Java设计模式的桥接模式怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java设计模式的桥接模式怎么实现”吧!什么是桥接模式桥接(Bridge)模式的定义如下:...
    99+
    2023-06-30
  • Java设计模式的代理模式怎么实现
    这篇文章主要介绍了Java设计模式的代理模式怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java设计模式的代理模式怎么实现文章都会有所收获,下面我们一起来看看吧。什么是代理模式代理模式的定义: 由于某...
    99+
    2023-06-30
  • Java设计模式之桥接模式怎么实现
    这篇文章主要介绍“Java设计模式之桥接模式怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java设计模式之桥接模式怎么实现”文章能帮助大家解决问题。一、什么是桥接模式:桥接,顾名思义,就是...
    99+
    2023-07-02
  • java设计模式之策略模式怎么实现
    这篇文章主要介绍了java设计模式之策略模式怎么实现,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。策略模式:策略模式是一种定义一系列算法的方法,算法完成的工作都是相同的工作,...
    99+
    2023-05-31
    java
  • java设计模式中如何实现单例模式
    这篇文章将为大家详细讲解有关java设计模式中如何实现单例模式,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。下面是一个简单的小实例://简单懒汉式 public class ...
    99+
    2023-05-30
    java
  • java设计模式之怎么实现单例模式
    这篇文章主要介绍了java设计模式之怎么实现单例模式的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java设计模式之怎么实现单例模式文章都会有所收获,下面我们一起来看看吧。单元素的枚举类型经常成为实现 Sing...
    99+
    2023-07-04
  • Java设计模式的桥接模式如何实现
    本文小编为大家详细介绍“Java设计模式的桥接模式如何实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java设计模式的桥接模式如何实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是桥接模式桥接(Bri...
    99+
    2023-07-06
  • Java设计模式之中介者模式的实现方式
    目录介绍实现总结介绍 Java中介者模式(Mediator Pattern)是一种行为设计模式,它可以降低多个对象之间的耦合性,通过一个中介者对象来协调这些对象的交互. 在中介者模式...
    99+
    2023-05-18
    Java设计模式中介者模式 Java中介者模式
  • java单例模式如何实现
    本篇内容主要讲解“java单例模式如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java单例模式如何实现”吧!0x01 宫女请安在朕的后宫中,皇后当之无愧的是天下第一(朕只能当...
    99+
    2023-06-19
  • java策略模式如何实现
    本篇内容介绍了“java策略模式如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 刘备要到江东娶老婆了,走之前诸葛亮给赵云(...
    99+
    2023-06-19
  • Java代理模式如何实现
    这篇文章主要介绍“Java代理模式如何实现”,在日常操作中,相信很多人在Java代理模式如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java代理模式如何实现”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作