熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

校驗值對象——應用Visitor模式和反射

2022-06-13   來源: Java高級技術 

概要
值對象是一些單一的參數用來聯合一系列的對象——在大多數的情況下在一個方法調用裡有各種各樣的參數這些參數描述了一個大數量級的屬性通常這些屬性需要單獨檢測而且大多數情況下是檢測其是否為null通常這些檢測帶出來了大量的代碼行這篇文章描述了如何實現基於著名的Visitor模式和反射的值對象
 
在業務過程中你通常有一些屬性不能為空而另外一些則沒有這樣的要求在那些必須有實例的屬性的案例中你不得不實現如下所示的檢測
if( attribute == null )
{
     throw new AttributeIsNullException()
}
如果值對象有N個屬性你將會得到如下所示的代碼
if( attribute == null )
{
      throw new AttributeIsNullException()
}
if( attribute == null )
{
      throw new AttributeIsNullException()
}

if( attribute N == null )
{
      throw new AttributeNIsNullException()
}
結果一大堆的IF語句但是你不得不把它們全部打出來
現在假設校驗的數量從增加到因為有個新增的用例必須在一個循環中實現你是不是失去了勇氣?用來減少這些檢測的一個有效的方法是將他們從值對象類移到值對象的校驗類
從這個觀點看來你可能承認你永遠執行相同的檢測唯一的不同是屬性的名稱和類型在大多數情況下類型不讓人感興趣因為由編譯器檢測它還有重要的一點需要確認接收這些屬性的值的方法都由同一個名稱開始在我們的案例中是get
通過反射調用這些值對象的getters方法非常簡單如果你使用Eclipse例如你可以自動為所有的屬性產品setters和getters方法對於我們的attributegetter方法是getAttribute()setter方法為setAttribute(Integer attributeValue)如果attribute是Integer類型的屬性如果這些前提給定了的話你就能考慮一個一般的解決方案這篇文章解釋了如何使用Visitor模式和反射來實現這個一般的解決方案
 
框架類和接口
下面的類圖顯示了建立我們一般的校驗框架需要用到的類和接口之間的關系

注意你可以從Resources上下載這些類和接口
 
Validateable接口
Validateable接口有著和Visitable接口相同的功能那個定義的方法validateWith()是一個和Visitor模式裡的Visitable接口的accept()方法相似的方法通過validateWith()方法你就能夠校驗有著不同validators的值對象因為這個方法以IClassAttributeValidator接口的實現作為參數
 
IClassAttributeValidator接口
IClassAttributeValidator接口和Visitor模式的Visitor接口相對應其中的validate(AbstractParameter param)方法和Visitor接口的visit(object SomeObject)方法相似validate()方法的AbstractParameter參數類型允許我們通過任何AbstractParameter類的子類類型的參數訪問validate另外在validateWith()方法裡使用這個接口作為參數允許我們在將來改變使用的validator用這些改變的validator作為來滿足不同的validation的需求——例如除了null檢測以外在一個定義的值范圍測試參數屬性
 
AbstractParameter
AbstractParameter類實現了Validateable接口的validateWith()方法就像你將要看到的下面的代碼片斷一樣這個實現非常簡單這個方法僅僅是調用給定的validator的validate()方法並且傳遞參數對象到validator
public void validateWith( IClassAttributeValidator validator ) throws Exception
{
      validatorvalidate( this );
}
而且AbstractParameter也實現一些常用的其他方法受保護的方法addOptionalMethod()使得所有的子類型增加一些可選擇的方法到optionalGetAttributeMethods HashMap()繼承自AbstractParameter使得你能夠取得哪些可能傳遞null的getters方法就像你能想象到的一樣你能夠增加可選擇的方法例如到繼承自AbstractParameter的值對象的構造器裡
isOptionalMethod()方法用於檢測屬性是否已經被校驗過了
toString()方法實現了一些便利因為值對象可能是由很多屬性組成使得在AbstractParameter的子類裡不需要寫很多的Systemoutprintlns實現這個方法也是使用反射達到目的的
 
GenericClassAttributeValidator
GenericClassAttributeValidator類實現了IClassAttributeValidator接口的validate()方法這個類同時也實現了單態模式validate()的實現看起來象下面這樣
public synchronized void validate( AbstractParameter param ) throws AttributeValidatorException
{
      Class clazz = paramgetClass();
      Method[] methods = clazzgetMethods();

      //Cycle over all methods and call the getters!
      //Check if the getter result is null
      //If result is null throw AttributeValidatorException
      Iterator methodIter = ArraysasList( methods erator();
      Method method = null;
      String methodName = null;

      while ( methodIterhasNext() )
      {
            method = (Method) methodIternext();
            methodName = methodgetName();

            if ( methodNamestartsWith( get ) &&
            clazzequals( methodgetDeclaringClass() ) &&
                 !paramisOptionalMethod( methodName ) )
            {
                 Object methodResult = null;

                 try
                 {
                       methodResult = methodinvoke( param null );
                 }
                 catch ( IllegalArgumentException e )
                 {
                       throw new AttributeValidatorException( egetMessage() );
                 }
                 catch ( IllegalAccessException e )
                 {
                       throw new AttributeValidatorException( egetMessage() );
                 }
                 catch ( InvocationTargetException e )
                 {
                       throw new AttributeValidatorException( egetMessage() );
                 }

                 if ( methodResult == null )
                 {
                       String attributeName = methodNamesubstring( )toLowerCase() +
                       methodNamesubstring( methodNamelength() );
                       String className = clazzgetName();
                       className = classNamesubstring( classNamelastIndexOf( ) + );

                       Integer errorNumber = new Integer( );
                       throw new AttributeValidatorException( Error: + errorNumber +
                       + attributeName + in + className + is null!!!);
                 }
            }
      }
}
首先就像你在代碼裡看到的那樣我們從值對象裡取得所有的方法然後我們遍歷所有方法的集合如果方法以get開頭便是AbstractParameter的子類型而不是可選擇的方法我們通過反射調用getter方法並且檢測它的結果如果結果是null那麼這就是一個錯誤如果不是便是正常情況那些可選擇的方法和繼承自父類的方法不會被執行
 
測試我們的類
現在我們實現了我們所需要的所有的類和接口我們必須做一些測試來檢驗我們的類是否能夠正常工作為了做到這一點我們寫了一點小的測試類和一個main方法來運行測試
 
TestParameter
TestParameter類繼承自AbstractParameter並且包括了一些需要校驗的私有屬性很簡單的個Integer屬性
 
Optional attributes
為了識別可選的屬性沒有被檢測我們定義了為屬性testParam可選的getter方法為了這個目的我們通過TestParameter的構造器裡的addOptionalMethod(methodName)方法將這個getter方法輸入到父類AbstractParameter的可選方法map裡
 
校驗框架是如何工作的
為了測試我們在TestParameter裡使用如下方式輸入
TestParameter param = new TestParameter( );
paramsetTestParam( new Integer( ) );
paramsetTestParam( new Integer( ) );
paramsetTestParam( new Integer( ) );
paramsetTestParam( new Integer( ) );
就像你所看到的那樣個Integer屬性記作Integer為了校驗我們僅僅調用paramvalidateWith( GenericClassAttributeValidatorgetInstance( ) );
這個校驗的結果是
testParam:
testParam:
testParam:
testParam屬性沒有被校驗因為我們記錄了它的getter方法為可選的其他所有的方法得到了校驗並且結果是正常的現在我們希望看到其中的一個屬性值為空這樣我們就能檢測是否validator能夠檢測到這個錯誤我們注釋掉下面的行
paramsetTestParam( new Integer( ) );
我們重新開始測試以後得到如下的結果
testParam:
Error: testParam in TestParameter is null!!!
testParam:
現在我們看到了validator已經檢測到了這個沒有賦值的屬性
 
如果屬性類型為集合類型將會怎麼樣呢?
如果屬性類型為集合它仍然會檢測這個集合是否為空但是可能檢測集合是否為null並不是你想要的在大多數情況下你希望檢測集合裡的對象是否為null如果集合的實現不允許null對象你不需要關心這些在GenericClassAttributeValidator裡的null對象繼承自AbstractParameter一些為集合保持繼承自AbstractParameter的對象的輔助代碼看起來如下所示
if ( methodResult instanceof Collection )
{
      Collection col = (Collection) methodResult;
      Iterator iter = erator();
      Object subParam = null;

      while ( iterhasNext() )
      {
            subParam = iternext();

            if ( subParam instanceof AbstractParameter )
            {
                 AbstractParameter abstractParam = ( AbstractParameter ) subParam;
                 abstractParamvalidateWith( this );
            }
      }
}
集合裡的所有沒有繼承自AbstractParameter類的對象沒有被檢測因為我們將使用一個不允許null對象的集合實現所以集合實現為我們完成了檢測如果你決定使用一個允許null對象的實現那麼為while循環的所有其他的對象的一個額外的null檢測就是必須的了
else if( subParam == null )
{
      Systemoutprintln( Error: SubParameter not set in Collection! );
}
 
值之間的依賴
在一些情況下只有當值對象的其他屬性被分配了值一個屬性才有可能是可選的屬性的可選性 sometimesOptional依賴於actionType屬性的值可能action屬性持有的值代表了actions例如addSomething = updateSomething = 和 deleteSomthing = 如果action的值是或者sometimesOptional屬性不是可選的如果action的值是則是可選的當我們為actionType賦值的時候我們必須設置sometimesOptional的可選性
public void setActionType(int actionType)
{
      thisactionType = actionType;
      superclearOptionalMethods( );
      switch( thisactionType )
      {
            case ActionParameterACTION_ADD :
            superaddOptionalMethod( getSometimesOptional );
            superaddOptionalMethod( getSometimesOptional );
            break;
            case ActionParameterACTION_UPDATE :
            superaddOptionalMethod( getSometimesOptional );
            break;
            case ActionParameterACTION_REMOVE :
            superaddOptionalMethod( getSometimesOptional );
            superaddOptionalMethod( getSometimesOptional );
            superaddOptionalMethod( getSometimesOptional );
            break;
            default :
            break;
      }
}
你會看到清除可選方法列表是必需的因為如果你給actionType賦值超過一次的話越來越多的方法將作為可選的方法添加進來另外的一個解決方法包括實現一個AddActionParameter一個UpdateActionParameter和一個RemoveActionParameter它們都是從AbstractParameter類繼承得來那麼你可能不需要actionType屬性但是擁有actionType屬性的類存在並且經常被使用對該類使用反射非常容易你必須使用Switch語句
 
展望
現在我們可以考慮繼承AbstractParameter的更多的功能——例如范圍校驗AbstractParamter需要一個數據結構來存儲范圍值HashMap能夠做到它以方法名作為key存儲范圍對象或者你可以檢測是否一個String類型的值包含一些定義的字等等你也可以考慮Perl 的正則表達式所有的這些檢測都可以在Validator類裡實現它實現了IClassAttributeValidator接口如果你想使用屬性的null檢測和附加值檢測那麼你可以寫一個子類來繼承GenericClassAttributeValidator
在JEE應用裡值對象經常被用來在客戶端和服務器之間傳遞業務過程的數據但是如果你僅僅在服務器端校驗這些值對象的屬性你常常不得不因為一個錯誤的非可選屬性為null而取消業務過程你必須中斷業務過程而向客戶端發送一個錯誤頁面這是一個好的實踐在服務端應用這些validators的同時你也可以以委派的形式在客戶端應用它們在客戶端檢測值對象可以避免不必要的對服務器的請求和降低網絡堵塞
 
寫一次使用多次
如果你使用我描述的方法來檢測你的值對象的屬性你可以永遠只增加值對象的屬性——不用改變validator它們能夠被自動檢測是否為null你也可以不用改變validator而改變一個屬性的條件而且當然一個已有的validator也能夠校驗一個未來的值對象如果這個值對象繼承AbstractParameter的話還有你也可以不用改變值對象而寫一個額外的validators因為validators實現的是Visitor模式這就是所謂的寫一次使用多次
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27352.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.