2015-05-14 128 views
0

我有一組上的業務邏輯驗證的要求:如何在Java中實現動態責任鏈?

  1. 驗證的每個獨立步驟必須分開;
  2. 這些步驟的順序可以由管理員定義;
  3. 可以禁用步驟。
  4. 每個驗證步驟不是用戶定義的 - 即代碼是編譯的

所以我想實現一個動態的責任鏈,它會從表中載入步驟順序和類名,用Class.forName()實例化它們。但我不喜歡將className存儲在表中,因爲這可能會導致潛在的問題(重構驗證器的名稱,例如,會簡單地破壞代碼)。這是我做的:

Enter image description here

當然,更靈活的解決方案必須是,更復雜的將是。不過,我想知道是否有一種方法來保存上述要求而不將類名存儲在表中?

+0

以及在你可以有供應商的應用程序的加載提供的類的實例,以您的應用程序,在這裏你只能從實例化它們。這將消除forname的要求,並要求你只需要引用類對象。 這一切都取決於它是如何提供給您的主要邏輯驗證代碼。 – Tschallacka

回答

4

你不需要重新發明輪子。您可以使用Apache的Commons Chain作爲起點,並與您的定製解決方案混合使用。它提供了一種優雅和簡單的方法來解決您的問題。 如果需要,您還可以將配置保存到XML文件(目錄)中並從您的數據庫中加載它。

下面是一個例子:

要查看共享鏈是如何工作的,讓我們開始有點人爲的例子:通過二手車的承辦商所採用的業務流程(也叫做,二手車銷售員)。下面是構成銷售過程中的步驟:

Get customer information 
Test-drive vehicle 
Negotiate sale 
Arrange financing 
Close sale 

現在假設你想使用模板方法模式這個流程建模。你可以創建一個抽象類 - 定義算法 - 這看起來是這樣的:

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(); 
} 

現在讓我們看看你能如何使用共享鏈實現這個過程。首先,下載Commons Chain。您可以抓取最新的夜間下載文件作爲.zip或.tar文件,也可以通過檢出CVS或SubVersion源代碼庫中的Commons Chain模塊獲取最新的代碼。提取歸檔文件,將commons-chain.jar文件放在你的類路徑中。

要實現使用共享鏈的業務流程,實施過程中的每個步驟時有一個單一的公共「做到這一切」命名的執行方法()的類。這是Command模式的傳統用法。以下是「獲取客戶信息」步驟的簡單實施。

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; 
    } 
} 

爲了說明的目的,這門課沒有太多的工作。但是,它確實將客戶的名稱存儲在上下文中。 Context對象提供了命令之間的粘合。就目前而言,將上下文看作只是一個哈希表,您可以將值填入數值中,並通過鍵將值拉出。所有後續命令現在都可以訪問這些數據。 TestDriveVehicle,NegotiateSale和ArrangeFinancing命令類是簡單的實現,只需打印出命令將執行的操作。

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實現使用上下文來提取在GetCustomerInfo命令中設置的客戶名稱。

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; 
    } 
} 

現在您可以將過程定義爲序列或「命令鏈」。

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); 
    } 
} 

此示例顯示如何使用Commons Chain API創建和執行一系列命令。 當然,就像現在用Java編寫的幾乎所有新軟件一樣,Commons Chain可以通過XML文件進行配置。 將此功能應用於「賣車」流程,現在可以在XML文件中定義命令序列。 該文件的規範名稱是chain-config.xml。

<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> 

鏈配置文件可以包含多個鏈組合到目錄中的鏈定義。對於這個例子,鏈定義是在默認目錄中定義的。實際上,您可以在此文件中擁有多個已命名的目錄,每個目錄都有自己的一組鏈。

現在,您不需要像SellVehicleChain中那樣定義命令序列,而是使用Commons Chain提供的類加載目錄並檢索命名鏈。

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); 
    } 
} 

鏈使用Commons Digester來讀取和解析配置文件。 要使用此功能,您需要將Commons Digester .jar文件添加到您的類路徑中。 我使用1.6版,並沒有問題。 Digester取決於Commons Collections(我使用3.1版), Commons Logging(版本1.0.4)和Commons BeanUtils 1.7.0。您還需要將這些.jar添加到您的類路徑中。 將這些.jar文件添加到我的類路徑後,CatalogLoader成功編譯並運行。輸出與其他兩個測試產生的結果完全相同。

來源:http://www.onjava.com/pub/a/onjava/2005/03/02/commonchains.html

+0

有趣的是,我可以使用這個Apache Commons的Responsability實現鏈,謝謝!可悲的是我認爲使用XML配置文件不會解決問題,鏈順序必須由用戶定義... – Tarek

+0

@Tarek你不能創建一個UI來允許用戶定義鏈順序嗎? –

+0

是的,這就是我要做的,我只是擔心在表中使用類名。我可以使用一個枚舉,它將映射ID到類名,在這種情況下,ID將永遠不會被刪除,只能從視圖中刪除... – Tarek