小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Matrix - 與 Java 共舞 - 開始使用Commons Chain (第一部分)

 philip 2006-05-04
開始使用Commons Chain (第一部分)

作者:Bill Siggelkow(著有Jakarta Struts Cookbook)

譯者:niuji





版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
作者:Bill Siggelkow;niuji
原文地址:http://www./pub/a/onjava/2005/03/02/commonchains.html

中文地址:http://www./resource/article/44/44049_Commons+Chain.html
關(guān)鍵詞: Commons Chain



作為程序開發(fā)人員,我們經(jīng)常需要對一個(gè)實(shí)際上程序性的系統(tǒng)應(yīng)用面向?qū)ο蟮姆椒?。商業(yè)分析家和管理人員描述這樣的系統(tǒng)時(shí)通常不使用類層次和序列圖,而是使用流程圖和工作流圖表。但是不論如何,使用面向?qū)ο蟮姆椒ń鉀Q這些問題時(shí)會(huì)帶來更多的靈活性。面向?qū)ο蟮脑O(shè)計(jì)模式提供了有用的結(jié)構(gòu)和行為來描述這種順序的處理,比如模版方法(Template Method)[GoF]和責(zé)任鏈(Chain of Responsibility)[GoF]。

Jakarta Commons的子項(xiàng)目Chain將上述兩個(gè)模式組合成一個(gè)可復(fù)用的Java框架用于描述順序的處理流程。這個(gè)在Jakarta Commons project社區(qū)中開發(fā)的框架,已經(jīng)被廣泛的接受并且使用于許多有趣的應(yīng)用中,特別的是他被Struts和Shale應(yīng)用框架作為處理HTTP請求處理的基礎(chǔ)機(jī)制。你可以在需要定義和執(zhí)行一組連續(xù)的步驟時(shí)使用Commons Chain。

至于經(jīng)典設(shè)計(jì)模式,開發(fā)者和架構(gòu)師普遍使用模版方法(Template Method)造型順序處理。模版方法(Template Method)中使用一個(gè)抽象的父類定義使用的算法:處理的步驟,具體實(shí)現(xiàn)交給子類。當(dāng)然,父類也可以為算法所使用的方法提供一個(gè)缺省實(shí)現(xiàn)。

由于模版方法(Template Method)依賴?yán)^承——子類必須繼承定義了算法的父類——因此使用這個(gè)模式的軟件表現(xiàn)出緊耦合而且缺少靈活性。又由于實(shí)現(xiàn)類添加自己的行為前必須擴(kuò)展父類,溝每⑷嗽北幌拗樸誒嗖憒沃?,磦蝤限制另樈{蟶杓頻牧榛钚浴ommons Chain使用配置文件定義算法,在程序運(yùn)行時(shí)解析配置文件,從而很好的解決了這個(gè)問題。

現(xiàn)在來看一下Commons Chain是怎樣工作的,我們從一個(gè)人造的例子開始:二手車銷售員的商業(yè)流程。下面是銷售流程的步驟:
1.        得到用戶信息
2.        試車
3.        談判銷售
4.        安排財(cái)務(wù)
5.        結(jié)束銷售

現(xiàn)在假設(shè)使用模版方法(Template Method)造型這個(gè)流程。首先建立一個(gè)定義了算法的抽象類:

清單1
public abstract class SellVehicleTemplate {
        public void sellVehicle() {
        getCustomerInfo();
        testDriveVehicle();
        negotiateSale();
        arrangeFinancing();
        closeSale();
        }

        public abstract void getCustomerInfo();
        public abstract void testDriveVehicle();
        public abstract void negotiateSale();
        public abstract void arrangeFinancing();
        public abstract void closeSale();        
}


現(xiàn)在來看一下怎樣用Commons Chain實(shí)現(xiàn)這個(gè)流程。首先,下載Commons Chain。你可以直接下載最新的zip或tar文件,也可以從CVS或者SubVersion源碼庫檢出Commons Chain模塊得到最新的代碼。解壓縮打包文件,將commons-chain.jar放入你的classpath中。

使用Commons Chain實(shí)現(xiàn)這個(gè)商業(yè)流程,必須將流程中的每一步寫成一個(gè)類,這個(gè)類需要有一個(gè)public的方法execute()。這和傳統(tǒng)的命令模式(Command pattern)實(shí)現(xiàn)相同。下面簡單實(shí)現(xiàn)了“得到用戶信息”:

清單2
package com.jadecove.chain.sample;

import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;

public class GetCustomerInfo implements Command {
        public boolean execute(Context ctx) throws Exception {
                System.out.println("Get customer info");
                ctx.put("customerName","George Burdell");
                return false;
        }
}


由于只是演示,這個(gè)類并沒有做很多工作。這里將用戶名放入了Context對象ctx中。這個(gè)Context對象連接了各個(gè)命令。暫時(shí)先將這個(gè)對象想象成根據(jù)關(guān)鍵字存取值的哈希表。所有后來的命令可以通過它訪問剛才放入的用戶名。TestDriveVehicle,NegotiateSale和ArrangeFinancing命令的實(shí)現(xiàn)只是簡單的打印了將執(zhí)行什么操作。

清單3
package com.jadecove.chain.sample;

import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;

public class TestDriveVehicle implements Command {
        public boolean execute(Context ctx) throws Exception {
                System.out.println("Test drive the vehicle");
                return false;
        }
}

public class NegotiateSale implements Command {
        public boolean execute(Context ctx) throws Exception {
                System.out.println("Negotiate sale");
                return false;
        }
}

public class ArrangeFinancing implements Command {
        public boolean execute(Context ctx) throws Exception {
                System.out.println("Arrange financing");
                return false;
        }
}


CloseSale從Context對象中取出GetCustomerInfo放入的用戶名,并將其打印。
清單4
package com.jadecove.chain.sample;

import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;

public class CloseSale implements Command {
        public boolean execute(Context ctx) throws Exception {
                System.out.println("Congratulations "
                  +ctx.get("customerName")
                        +", you bought a new car!");
                return false;
        }
}


現(xiàn)在你可以將這個(gè)流程定義成一個(gè)序列(或者說“命令鏈”)。
清單5
package com.jadecove.chain.sample;

import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ContextBase;

public class SellVehicleChain extends ChainBase {
        public SellVehicleChain() {
                super();
                addCommand(new GetCustomerInfo());
                addCommand(new TestDriveVehicle());
                addCommand(new NegotiateSale());
                addCommand(new ArrangeFinancing());
                addCommand(new CloseSale());
        }
        public static void main(String[] args) throws Exception {
                Command process = new SellVehicleChain();
                Context ctx = new ContextBase();
                process.execute(ctx);
        }
}


運(yùn)行這個(gè)類將會(huì)輸出以下結(jié)果:
Get customer info
Test drive the vehicle
Negotiate sale
Arrange financing
Congratulations George Burdell, you bought a new car!


在進(jìn)一步深入之前,讓我們來看一下我們使用了的Commons Chain的類和接口。


圖1

Command類和Chain類的關(guān)系就是組合模式(Composite pattern)[GoF]的例子:Chain不僅由多個(gè)Command組成,而且自己也是Command。這使你可以非常簡單得將單個(gè)命令(Command)替換成由多個(gè)命令(Command)組成的鏈(Chain)。這個(gè)由Command對象唯一操作定義的方法代表了一個(gè)直接的命令:

public boolean execute(Context context);

參數(shù)context僅僅是一個(gè)存放了名稱-值對的集合。接口Context在這里作為一個(gè)標(biāo)記接口:它擴(kuò)展了java.util.Map但是沒有添加任何特殊的行為。于此相反,類ContextBase不僅提供了對Map的實(shí)現(xiàn)而且增加了一個(gè)特性:屬性-域透明。這個(gè)特性可以通過使用Map的put和get方法操作JavaBean的域,當(dāng)然這些域必須使用標(biāo)準(zhǔn)的getFoo和setFoo方法定義。那些通過JavaBean的“setter”方法設(shè)置的值,可以通過對應(yīng)的域名稱,用Map的get方法得到。同樣,那些用Map的put方法設(shè)置的值可以通過JavaBean的“getter”方法得到。

例如,我們可以創(chuàng)建一個(gè)專門的context提供顯式的customerName屬性支持。
清單6
package com.jadecove.chain.sample;

import org.apache.commons.chain.impl.ContextBase;

public class SellVehicleContext extends ContextBase {

        
        private String customerName;

        public String getCustomerName() {
                return customerName;
        }
        
        public void setCustomerName(String name) {
                this.customerName = name;
        }
}


現(xiàn)在你既可以進(jìn)行Map的一般屬性存取操作同時(shí)也可以使用顯式的JavaBean的訪問和修改域的方法,這兩個(gè)將產(chǎn)生同樣的效果。但是首先你需要在運(yùn)行SellVehicleChain時(shí)實(shí)例化SellVehiceContext而不是ContextBase。
清單7
public static void main(String[] args) throws Exception {
                Command process = new SellVehicleChain();
                Context ctx = new SellVehicleContext();
                process.execute(ctx);
        }


盡管你不改變GetCustomerInfo中存放用戶名的方法——仍然使用ctx.put("customerName", "George Burdell")——你可以在CloseSale中使用getCustomerName()方法得到用戶名。
清單8
        public boolean execute(Context ctx) throws Exception {
            SellVehicleContext myCtx = (SellVehicleContext) ctx;
            System.out.println("Congratulations "
                                   + myCtx.getCustomerName()
                                  + ", you bought a new car!");
            return false;
        }


那些依賴類型安全和context的顯式域的命令(Command)可以利用標(biāo)準(zhǔn)的getter和setter方法。當(dāng)一些新的命令(Command)被添加時(shí),它們可以不用考慮context的具體實(shí)現(xiàn),直接通過Map的get和put操作屬性。不論采用何種機(jī)制,ContextBase類都可以保證命令(Command)間可以通過context互操作。

下面這個(gè)例子展示了如何使用Commons Chain的API建立并執(zhí)行一組順序的命令。當(dāng)然,和現(xiàn)在大多數(shù)Java軟件一樣,Commons Chain可以使用XML文件作為配置文件。你可以將“汽車銷售”流程的步驟在XML文件中定義。這個(gè)文件有個(gè)規(guī)范的命名chain-config.xml。

清單9
<catalog>
  <chain name="sell-vehicle">
    <command   id="GetCustomerInfo"
        className="com.jadecove.chain.sample.GetCustomerInfo"/>
    <command   id="TestDriveVehicle"
        className="com.jadecove.chain.sample.TestDriveVehicle"/>
    <command   id="NegotiateSale"
        className="com.jadecove.chain.sample.NegotiateSale"/>
    <command   id="ArrangeFinancing"
        className="com.jadecove.chain.sample.ArrangeFinancing"/>
    <command   id="CloseSale"
        className="com.jadecove.chain.sample.CloseSale"/>
  </chain>
</catalog>


Chain的配置文件可以包含多個(gè)鏈定義,這些鏈定義可以集合進(jìn)不同的編目中。在這個(gè)例子中,鏈定義在一個(gè)默認(rèn)的編目中定義。事實(shí)上,你可以在這個(gè)文件中定義多個(gè)名字的編目,每個(gè)編目可擁有自己的鏈組。

現(xiàn)在你可以使用Commons Chain提供的類載入編目并得到指定的鏈,而不用像SellVehicleChain中那樣自己在程序中定義一組命令:
清單10
package com.jadecove.chain.sample;

import org.apache.commons.chain.Catalog;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.config.ConfigParser;
import org.apache.commons.chain.impl.CatalogFactoryBase;

public class CatalogLoader {
        private static final String CONFIG_FILE =
                "/com/jadecove/chain/sample/chain-config.xml";
        private ConfigParser parser;
        private Catalog catalog;
        
        public CatalogLoader() {
                parser = new ConfigParser();
        }
        public Catalog getCatalog() throws Exception {
                if (catalog == null) {
                
        parser.parse(this.getClass().getResource(CONFIG_FILE));                
        
                }
                catalog = CatalogFactoryBase.getInstance().getCatalog();
                return catalog;
        }
        public static void main(String[] args) throws Exception {
                CatalogLoader loader = new CatalogLoader();
                Catalog sampleCatalog = loader.getCatalog();
                Command command = sampleCatalog.getCommand("sell-vehicle");
                Context ctx = new SellVehicleContext();
                command.execute(ctx);
        }
}


Chain使用Commons Digester來讀取和解析配置文件。因此你需要將Commons Digester.jar加入classpath中。我使用了1.6版本并且工作得很好。Digester使用了Commons Collectios(我使用的版本是3.1),Commons Logging(版本1.0.4),Commons BeanUtils(1.7.0),因此你也需要將它們的jar文件加入classpath中。在加入這些jar后,CatalogLoader就可以被編譯和運(yùn)行,它的輸出和另外兩個(gè)測試完全相同。

現(xiàn)在你可以在XML文件中定義鏈,并可以在程序中得到這個(gè)鏈(別忘了鏈也是命令),這樣擴(kuò)展的可能性和程序的靈活性可以說是無限的。假設(shè)過程“安排財(cái)務(wù)”實(shí)際上由一個(gè)完全分離的商業(yè)部門處理。這個(gè)部門希望為這種銷售建立自己的工作流程。Chain提供了嵌套鏈來實(shí)現(xiàn)這個(gè)要求。因?yàn)殒湵旧砭褪敲?,因此你可以用指向另一個(gè)鏈的引用替換一個(gè)單一用途的命令。下面是增加了新流程的鏈的定義:
清單11
<catalog name="auto-sales">
   <chain name="sell-vehicle">
         <command   id="GetCustomerInfo"
                 className="com.jadecove.chain.sample.GetCustomerInfo"/>
         <command   id="TestDriveVehicle"
                 className="com.jadecove.chain.sample.TestDriveVehicle"/>
         <command   id="NegotiateSale"
                 className="com.jadecove.chain.sample.NegotiateSale"/>
         <command
                 className="org.apache.commons.chain.generic.LookupCommand"
             catalogName="auto-sales"
                      name="arrange-financing"
                  optional="true"/>
         <command   id="CloseSale"
                 className="com.jadecove.chain.sample.CloseSale"/>
   </chain>
   <chain name="arrange-financing">
         <command   id="ArrangeFinancing"
                 className="com.jadecove.chain.sample.ArrangeFinancing"/>
   </chain>
</catalog>


Commons Chain提供了一個(gè)常用的命令LookupCommand來查找和執(zhí)行另一個(gè)鏈。屬性optional用于控制當(dāng)指定的嵌套鏈沒有找到時(shí)如何處理。optional=true時(shí),即使鏈沒找到,處理也會(huì)繼續(xù)。反之,LookupCommand將拋出IllegalArgumentException,告知指定的命令未找到。

在下面三種情況下,命令鏈將結(jié)束:
1.        命令的execute方法返回true
2.        運(yùn)行到了鏈的盡頭
3.        命令拋出異常

當(dāng)鏈完全處理完一個(gè)過程后,命令就返回true。這是責(zé)任鏈模式(Chain of Responsibility)的基本概念。處理從一個(gè)命令傳遞到另一個(gè)命令,直到某個(gè)命令(Command)處理了這個(gè)命令。如果在到達(dá)命令序列盡頭時(shí)仍沒有處理返回true,也假設(shè)鏈已經(jīng)正常結(jié)束。

當(dāng)有命令拋出錯(cuò)誤時(shí)鏈就會(huì)非正常結(jié)束。在Commons Chain中,如果有命令拋出錯(cuò)誤,鏈的執(zhí)行就會(huì)中斷。不論是運(yùn)行時(shí)錯(cuò)誤(runtime exception)還是應(yīng)用錯(cuò)誤(application exception),都會(huì)拋出給鏈的調(diào)用者。但是許多應(yīng)用都需要對在命令之外定義的錯(cuò)誤做明確的處理。Commons Chain提供了Filter接口來滿足這個(gè)要求。Filter繼承了Command,添加了一個(gè)名為postprocess的方法。

public boolean postprocess(Context context, Exception exception);
只要Filter的execute方法被調(diào)用,不論鏈的執(zhí)行過程中是否拋出錯(cuò)誤,Commons Chain都將保證Filter的postprocess方法被調(diào)用。和servlet的過濾器(filter)相同,Commons Chain的Filter按它們在鏈中的順序依次執(zhí)行。同樣,F(xiàn)ilter的postprocess方法按倒序執(zhí)行。你可以使用這個(gè)特性實(shí)現(xiàn)自己的錯(cuò)誤處理。下面是一個(gè)用于處理我們例子中的錯(cuò)誤的Filter:
清單12
package com.jadecove.chain.sample;

import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;

public class SellVehicleExceptionHandler implements Filter {

        public boolean execute(Context context) throws Exception {
                System.out.println("Filter.execute() called.");
                return false;
        }

        public boolean postprocess(Context context,
                                 Exception exception) {
                if (exception == null) return false;
                System.out.println("Exception "
                              + exception.getMessage()
                              + " occurred.");
                return true;
        }
}


Filter在配置文件中的定義就和普通的命令(Command)定義相同:
清單13
<chain name="sell-vehicle">
  <command   id="ExceptionHandler"
     className =
           "com.jadecove.chain.sample.SellVehicleExceptionHandler"/>
  <command   id="GetCustomerInfo"
      className="com.jadecove.chain.sample.GetCustomerInfo"/>


Filter的execute方法按定義的序列調(diào)用。然而,它的postprocess方法將在鏈執(zhí)行完畢或拋出錯(cuò)誤后執(zhí)行。當(dāng)一個(gè)錯(cuò)誤被拋出時(shí),postprocess方法處理完后會(huì)返回true,表示錯(cuò)誤處理已經(jīng)完成。鏈的執(zhí)行并不會(huì)就此結(jié)束,但是本質(zhì)上來說這個(gè)錯(cuò)誤被捕捉而且不會(huì)再向外拋出。如果postprocess方法返回false,那錯(cuò)誤會(huì)繼續(xù)向外拋出,然后鏈就會(huì)非正常結(jié)束。

讓我們假設(shè)ArrangeFinancing因?yàn)橛脩粜庞每〒p壞拋出錯(cuò)誤。SellVehicleExceptionHandler就能捕捉到這個(gè)錯(cuò)誤,程序輸出如下:
Filter.execute() called.
Get customer info
Test drive the vehicle
Negotiate sale
Exception Bad credit occurred.


結(jié)合了過濾器(filter)和子鏈技術(shù)后,你就可以造型很復(fù)雜的工作流程。

Commons Chain是一個(gè)很有前途的框架,現(xiàn)在仍在開發(fā),新的功能被頻繁地添加到其中。在下一篇關(guān)于Commons Chain的文章中,我們將研究Struts 1.3中是如何使用Commons Chain的。

Struts 1.3中用完全使用Commons Chain的類替換了原來的處理HTTP請求的類。如果你以前自己定制過Struts的請求處理(request processor),你將發(fā)現(xiàn)處理這個(gè)問題時(shí)Commons Chain為程序帶來了很好的靈活性。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多