JavaTip:實(shí)現(xiàn)Command模式(轉(zhuǎn))-創(chuàng)新互聯(lián)

Java Tip: 實(shí)現(xiàn)Command模式 (轉(zhuǎn))[@more@]

Java Tip: 實(shí)現(xiàn)Command模式

創(chuàng)新互聯(lián)自2013年起,先為高縣等服務(wù)建站,高縣等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為高縣企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。

概述
有時(shí)需要向?qū)ο蟀l(fā)送請(qǐng)求,但是不知道 "被請(qǐng)求的操作" 或 "請(qǐng)求的接受者" 的任何信息。在面向過(guò)程的程序設(shè)計(jì)語(yǔ)言中,這類通信是通過(guò)回調(diào)函數(shù)來(lái)完成的:在某個(gè)地方登記這個(gè)函數(shù),然后在后面調(diào)用它。在面向?qū)ο蟪绦蛑?,command(命令)與回調(diào)函數(shù)等價(jià),它封裝了回調(diào)函數(shù)。本文演示如何在Java中實(shí)現(xiàn)Command模式。

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

設(shè)計(jì)模式不但可以加速面向?qū)ο蠊こ痰脑O(shè)計(jì)進(jìn)度,而且可以提高開(kāi)發(fā)小組的產(chǎn)出以及軟件的質(zhì)量。Commad模式是一種對(duì)象行為模式,它可以對(duì)發(fā)送者(sender)和接收者(receiver)完全解耦(decoupling)。("發(fā)送者" 是請(qǐng)求操作的對(duì)象,"接收者" 是接收請(qǐng)求并執(zhí)行某操作的對(duì)象。有了 "解耦",發(fā)送者對(duì)接收者的接口一無(wú)所知。)這里,"請(qǐng)求"(request)這個(gè)術(shù)語(yǔ)指的是要被執(zhí)行的命令。Command模式還讓我們可以對(duì) "何時(shí)" 以及 "如何" 完成請(qǐng)求進(jìn)行改變。因此,Command模式為我們提供了靈活性和可擴(kuò)展性。

在象C這樣的程序設(shè)計(jì)語(yǔ)言中,函數(shù)指針常被用來(lái)消除龐大的switch語(yǔ)句。(參見(jiàn) "Java Tip 30: Polymorphism and Java" 獲得更詳細(xì)的介紹)Java沒(méi)有函數(shù)指針,所以我們可以用Command模式來(lái)實(shí)現(xiàn)回調(diào)函數(shù)。在下面的第一個(gè)代碼示例TestCommand.java中,你將實(shí)際看到它是如何實(shí)現(xiàn)的。

一些開(kāi)發(fā)者在其它語(yǔ)言中已經(jīng)習(xí)慣于使用函數(shù)指針,因而他們往往禁不起誘惑,想以同樣的方法使用Reflection api的Method對(duì)象。例如,在 "Java Reflection" 一文中,Paul Tremblett介紹了如何使用Reflection而不是switch語(yǔ)句來(lái)實(shí)現(xiàn)事務(wù)處理(Transaction) (參見(jiàn) "相關(guān)資源" 獲得Tremblett的文章和Sun的Reflection教程網(wǎng)址的鏈接)。我是不會(huì)為這樣的誘惑所動(dòng)的。正如Sun所指出的:在有其它更貼近于Java程序設(shè)計(jì)語(yǔ)言的工具滿足使用要求的情況下,一般不提倡使用Reflect API。不使用Method對(duì)象,程序會(huì)更易于調(diào)試和維護(hù)。所以,你應(yīng)該定義一個(gè)接口,并在類中實(shí)現(xiàn)它,以執(zhí)行所需操作。

因此我建議,可以使用Command模式并結(jié)合Java的動(dòng)態(tài)加載和綁定機(jī)制來(lái)實(shí)現(xiàn)函數(shù)指針。(關(guān)于Java的動(dòng)態(tài)加載和綁定機(jī)制的詳細(xì)介紹,參見(jiàn)Gosling和Henry McGilton的"The Java Language Environment -- A White Paper",列于"相關(guān)資源"。)

遵循上面的建議,我們可以運(yùn)用Command模式,利用它所提供的多態(tài)性來(lái)消除龐大的switch語(yǔ)句,從而設(shè)計(jì)出可擴(kuò)展的系統(tǒng)。我們還可以利用Java獨(dú)有的動(dòng)態(tài)加載和綁定機(jī)制來(lái)構(gòu)筑動(dòng)態(tài)的、并且可以動(dòng)態(tài)擴(kuò)展的系統(tǒng)。這一點(diǎn)在下面第二個(gè)代碼示例TestTransactionCommand.java中進(jìn)行說(shuō)明。

Command模式使請(qǐng)求本身成為一個(gè)對(duì)象。這個(gè)對(duì)象和其它對(duì)象一樣可以被存儲(chǔ)和四處傳遞。這種模式的關(guān)鍵在于一個(gè)Command接口:它聲明了一個(gè)接口,用于執(zhí)行操作。最簡(jiǎn)單的形式下,這個(gè)接口包含一個(gè)抽象的execute操作。每個(gè)具體的Command類把接收者作為一個(gè)實(shí)例變量進(jìn)行存儲(chǔ),從而指定了一對(duì) "接收者" 和 "行為"。它為execute()方法提供不同的實(shí)現(xiàn)以進(jìn)行請(qǐng)求調(diào)用。接收者知道如何執(zhí)行請(qǐng)求。

如下的圖1表示了Switch--一個(gè)Command對(duì)象的集合體(aggregation)。它的接口中有flipUp()和flipDown()兩種操作。Switch被稱為 "調(diào)用者"(invoker),因?yàn)樗{(diào)用command接口中的execute操作。

具體的command,如LightOnCommand,實(shí)現(xiàn)command接口的execute操作。它知道去調(diào)用合適的接收者對(duì)象的操作。這種情況下它充當(dāng)了一個(gè)適配器(adapter)。通過(guò) "適配器"這一術(shù)語(yǔ), 我想說(shuō)明:具體的Command對(duì)象是一個(gè)簡(jiǎn)單的連接器,它連接具有不同接口的 "調(diào)用者" 和 "接收者"。

客戶實(shí)例化調(diào)用者,接收者以及具體的command對(duì)象。

圖2為時(shí)序圖,它表示對(duì)象間的相互作用。它說(shuō)明了Command如何對(duì)調(diào)用者和接收者(以及它執(zhí)行的請(qǐng)求)解耦。客戶用合適的接收者作為構(gòu)造函數(shù)的參數(shù)來(lái)創(chuàng)建具體的command。然后,它將Command保存在調(diào)用者中。調(diào)用者回調(diào)具體的command,后者知道如何完成想要的Action()操作。

客戶(代碼中的主程序)創(chuàng)建具體的Command對(duì)象并設(shè)置它的接收者。作為一個(gè)調(diào)用者對(duì)象,Switch保存具體的Command對(duì)象。調(diào)用者通過(guò)對(duì)Command對(duì)象調(diào)用execute來(lái)發(fā)送請(qǐng)求。具體的Command對(duì)象對(duì)它的接收者進(jìn)行操作調(diào)用,從而完成操作請(qǐng)求。

這里最關(guān)鍵的思想在于,具體的command用調(diào)用者來(lái)注冊(cè)自身,調(diào)用者進(jìn)行回調(diào),在接收者身上執(zhí)行命令。

Command模式示例代碼
讓我們來(lái)看一個(gè)簡(jiǎn)單的例子,它通過(guò)Command模式實(shí)現(xiàn)回調(diào)機(jī)制。

以Fan(風(fēng)扇)和Light(燈)為例。我們的目標(biāo)是設(shè)計(jì)一個(gè)Switch,它可以對(duì)任一個(gè)對(duì)象進(jìn)行 "開(kāi)" 和 "關(guān)" 的操作。Fan和Light具有不同的接口,這意味著Switch必須獨(dú)立于接收者接口,或者說(shuō),它不清楚接收者的接口。為了解決這一問(wèn)題,每個(gè)Switch都需要合適的command作為參數(shù)。很明顯,連接到Light的Switch和連接到Fan的Switch具有不同的command。因此,Command類必須是抽象的,或者是個(gè)接口。

Switch的構(gòu)造函數(shù)被調(diào)用時(shí),它以一組合適的command作為參數(shù)。command作為Switch的私有變量保存下來(lái)。

調(diào)用flipUp()和flipDown()操作時(shí),它們只是簡(jiǎn)單地讓合適的command進(jìn)行execute()操作。Switch對(duì)調(diào)用execute()后將發(fā)生些什么一無(wú)所知。

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模式將 "調(diào)用操作的對(duì)象" (Switch)和 "知道如何執(zhí)行操作的對(duì)象" (Light和Fan)完全分離開(kāi)來(lái)。這帶來(lái)了很大的靈活性:發(fā)送請(qǐng)求的對(duì)象只需要知道如何發(fā)送;它不必知道如何完成請(qǐng)求。

Command模式實(shí)現(xiàn)Transaction
Command模式也被稱為action(動(dòng)作)模式或transaction(事務(wù))模式。假設(shè)有一個(gè)服務(wù)器,它接收并處理客戶通過(guò)TCP/IP socket連接發(fā)送的transaction。這些transaction包含一個(gè)命令,后跟零個(gè)或多個(gè) 參數(shù)。

一些設(shè)計(jì)者可能會(huì)使用switch語(yǔ)句,每個(gè)command對(duì)應(yīng)一個(gè)case。在一個(gè)面向?qū)ο蠊こ痰脑O(shè)計(jì)中,代碼中如果使用switch語(yǔ)句,往往表示這是一個(gè)糟糕的設(shè)計(jì)。Command模式展現(xiàn)的是支持transaction的面向?qū)ο蟮姆椒ǎ梢杂糜诮鉀Q這類設(shè)計(jì)問(wèn)題。

在TestTransactionCommand.java程序的客戶代碼中,所有請(qǐng)求都被封裝在通用的TransactionCommand對(duì)象中。TransactionCommand對(duì)象由客戶創(chuàng)建并用CommandManager進(jìn)行登記。等待的請(qǐng)求可以通過(guò)調(diào)用runCommands()在不同時(shí)期被執(zhí)行,這帶來(lái)了很大的靈活性。而且我們還可以將多個(gè)command組裝成一個(gè)復(fù)合command。示例代碼中還有CommandArgument,CommandReceiver,CommandManager這些類,以及TransactionCommand的子類--AddCommand和SubtractCommand。下面是對(duì)這些類的介紹。

· CommandArgument是一個(gè)helper類,它保存命令的參數(shù)。如果是大量(或可變數(shù)量)的任何類型的參數(shù),它可以被重寫(xiě),以簡(jiǎn)化參數(shù)的傳遞工作。

· CommandReceiver實(shí)現(xiàn)所有的命令處理方法(command-processing method),它用Singleton模式來(lái)實(shí)現(xiàn)。

· CommandManager是調(diào)用者,和前面例子中的Switch相當(dāng)。它在其私有myCommand變量中保存通用TransactionCommand對(duì)象。runCommands()被調(diào)用時(shí),它調(diào)用合適的TransactionCommand對(duì)象的execute()。

Java中,可以根據(jù)一個(gè)包含類名的字符串查找類的定義。在TransactionCommand類的execute ()操作中,我先計(jì)算出類名,然后將它鏈接到運(yùn)行系統(tǒng)中--也就是說(shuō),類是根據(jù)需要被即時(shí)載入的。這里所采用的命名方式是,在命令名后連接一個(gè) "Command" 字符串作為transaction command子類名,這樣它就可以被動(dòng)態(tài)載入。

注意,newInstance()返回的Class對(duì)象必須被轉(zhuǎn)換為合適的類型。這意味著新的類要么必須實(shí)現(xiàn)一個(gè)接口,要么繼承一個(gè)在編譯期就為程序所知道的現(xiàn)有的類。本例中我們是實(shí)現(xiàn)Command接口,所以不存在問(wèn)題。
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();
  }

命令及其參數(shù)保存在列表中,并被封裝成通用TransactionCommand對(duì)象。通用TransactionCommand用CommandManager來(lái)注冊(cè)。任何時(shí)候,命令可以在CommandManager類中通過(guò)調(diào)用runCommands()接口來(lái)執(zhí)行。

客戶代碼不依賴于任何具體的TransactionCommand子類,也就是說(shuō),我的設(shè)計(jì)是針對(duì)接口而不是實(shí)現(xiàn)。這帶來(lái)了靈活性:要想增加一個(gè)新的命令,只需要定義一個(gè)新的TransactionCommand子類,并在CommandReceiver類中提供新的命令處理方法的實(shí)現(xiàn)。僅此而已。

結(jié)論
Command模式具有以下優(yōu)點(diǎn):

1. command將 "進(jìn)行操作請(qǐng)求" 的對(duì)象和 "知道如何執(zhí)行操作" 的對(duì)象分離開(kāi)來(lái)(即,解耦)。

2. command是個(gè)很棒的對(duì)象。它可以象任何其它對(duì)象一樣被使用和繼承。

3. 多個(gè)command可以被組裝成一個(gè)復(fù)合command。

4. 很容易增加新的command,因?yàn)椴恍枰薷默F(xiàn)有的類。

如果執(zhí)行過(guò)的命令序列被保存在一個(gè)歷史列表中,就可以遍歷這個(gè)列表來(lái)提供undo和redo操作。要想實(shí)現(xiàn)這一功能,必須在Command接口中有一個(gè)unexecute()操作。這一練習(xí)留給讀者自己去完成。

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

相關(guān)資源
· 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/


網(wǎng)頁(yè)名稱:JavaTip:實(shí)現(xiàn)Command模式(轉(zhuǎn))-創(chuàng)新互聯(lián)
URL標(biāo)題:http://bm7419.com/article40/cdieho.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站響應(yīng)式網(wǎng)站、網(wǎng)站營(yíng)銷移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站收錄、面包屑導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)