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

Java中利用Reflection API優化代碼

2022-06-13   來源: Java核心技術 

  摘要
  開發者通過各種各樣的方法來嘗試避免單調冗余的編程一些編程的規則例如繼承多態或者設計模型可以幫助開發者避免產生多余的代碼不過由於軟件開發方面存在著不確定性因此這些規則並不能消除代碼維護和重新編寫的需要在很多時候維護都是不可避免的只有不能運作的軟件才是從不需要維護的不過這篇文章介紹了你可以使用Java的Reflection API的功能來減少單調的代碼編寫並可以使用活動的代碼產生來克服reflection的限制
  數據配置(由外部的源頭得到數據並且將它裝載到一個Java對象中)可以利用reflection的好處來創建一個可重用的方案問題是很簡單的將數據由一個文件裝入到一個對象的字段中現在假設用作數據的目標Java類每星期改變一次?有一個很直接的解決方法不過你必須不斷地維護載入的過程來反映任何的改變在更復雜的環境下同樣的問題可能會令系統崩潰掉對於一個處理過運用XML的大型系統的人來說他就會遇到過這個問題要編寫一個載入的過程通常是非常單調乏味的由於數據源或者目標Java類的改變你需要經常更新和重新編寫代碼在這裡我要介紹另一個解決方案那就是使用映射它通常使用更少的編碼並且可以在目標Java類發生改變後更新自己
  最初我想介紹一個使用Reflection在運行期間配置數據的方案在開始的時候一個動態基於映射的程序要比一個簡單的方法更有吸引力多了隨後我要揭示出運行時Reflection的復雜性和冒險性這篇文章將介紹由運行時的Reflection到活動的代碼產生
  由簡單到復雜
  我的第一個方案使用一個載入類將數據從一個文件載入到對象中我的源代碼含有對StringTokenizer對象下一節點方法的多次調用在修改多次後我的編碼邏輯變得非常的直接系統化該類構造了專用的代碼在這個初始方案中我只需要使用個基本的對象
  Strings
  Objects
  Arrays of objects
  你可以影射類的對象來產生代碼塊如下表所示
  被影射來產生代碼塊的對象
  
  我已經使用這個方案作了幾次編碼因此我在寫代碼之前我已經知道該方案和代碼的結構難點在於該類是變化的類的名字成份和結構在任何時候都可能發生變化而任何的改變你都要重新編寫代碼雖然會發生這些變化但是結構和下載的流程仍然是一樣的在寫代碼前我仍然知道代碼的結構和成份我需要一個方法來將頭腦中的編碼流程轉換為一個可重用的自動的形式由於我是一個有效率的編程者我很快就厭倦了編寫幾乎一樣的代碼這時我想到了映射
  數據配置通常需要一個源到目的數據的影射影射可以是一個圖解DTD(document type definition文檔類型定義)文件格式等在這個例子中映射將一個對象的類定義解釋為我們要映射的流程映射可以在運行時復制代碼的功能在需要重寫代碼時我將載入的過程用映射來代替它所需要的時間和重寫是一樣的
  載入的工程可以概括為以下幾步
  解釋一個影射決定你在構造一個對象時需要些什麼
  請求數據要滿足構造的需要要進行一個調用來得到數據
  數據由源中得到
  數據被填充入一個對象的新實例
  如果必要的話重復步驟
  你需要以下的類來滿足以上的步驟
  .數據類(Data classes)由ASCII文件中的數據實例化類定義提供數據的影射數據類必須滿足以下的條件
  它們必須包含有一個構造器來接收全部必需的參數以使用一個有效的狀態來構造對象
  它們必須由對象構成這些對象是reflective過程知道如何處理的
  .對象裝載器(Object loader)使用reflection和數據類作為一個影射來載入數據產生數據請求
  .載入管理器(Load manager)作為對象裝載器和數據源的中介層將對數據的請求轉換為一個數據指定的調用這可以令對象載入器做到與數據源無關通過它的接口和一個可載入的類對象通信
  .數據循環接口(Data iterator interface)載入管理器和載入類對象使用這個接口來由數據源中得到數據
  一旦你創建了支持的類你就可以使用以下的聲明來創建和影射一個對象
  FooFileIterator iter = new FooFileIterator(fileLocation log);
  LoadManager manager = new FooFileLoadManager(iter);
  SubFooObject obj =
  (SubFooObject)ReflectiveObjectLoaderinitializeInstance(SubFooObjectclass managerlog);
  通過這個處理你就創建了一個包含有文件內容的SubFooObject實例
  局限
  開發者必須決定使用哪個方案來解決問題是最好的通常做出這個決定是最困難的部分在考慮使用reflection作數據配置時你要考慮到以下一些限制
  不要令一個簡單的問題復雜化reflection是比較復雜的因此在必要的時候才使用它一旦開發者明白了reflection的能力他就想使用它來解決所有的問題如果你有更快更簡單的方案來解決問題時你就不應該使用reflection(即使這個更好的方案可能使用更多的代碼)reflection是強大的但也有一些風險
  考慮性能reflection對性能的影響比較大因為要在運行時發現和管理類屬性需要時間和內存
  重新評估方案
  如上所述使用運行時reflection的第一個限制是不要令簡單的問題復雜化在使用reflection時這是不可避免的將reflection和遞歸結合起來是一個令人頭痛的問題重新看代碼也是一件可怕的事情而准確決定代碼的功能也是非常復雜的要知道代碼的准確作用的唯一方法是使用一些取樣數據逐行地看就象運行時一樣不過對於每個可能的數據組合都使用這種方式幾乎是不可能的在這種情況下使用單元測試代碼可能有些幫助不過也很可能出現錯誤幸運的是還有一個可選的方法
  可選的方法
  由上面列出的限制可以看到在某些情況下使用reflective載入過程可能是得不償失的代碼產生提供了一個通用的選擇方法你也可以使用reflection來檢查一個類並且為載入過程產生代碼
  Andrew Hunt和David Thomas介紹了兩類的代碼產生器見The Pragmatic Programmer(/jwl#resources)
  Passive(被動)被動的代碼產生器在實現代碼時需要人工的干預許多的IDE(集成開發環境)都提供相應的向導來實現
  Active(主動)主動的代碼產生指的是代碼一旦創建就不再需要修改了如果有問題產生這個問題也應該在代碼產生器中解決而不是在產生的源文件中解決在理想的情況下這個過程應該包含在編譯的處理過程中從而確保類不會過期
  代碼產生的優點和缺點包含有以下方面
  優點
  .簡單產生的代碼通常是更便於開發者閱讀和調試
  .編譯過程的錯誤Reflexive在運行時出現錯誤的機會要比編譯的期間多例如改變被載入的對象將有可能令產生的載入類拋出一個編譯的錯誤不過reflexive過程將不會看到任何的區別直到在運行時遇到這個類
  缺點
  .維護使用被動的代碼產生修改被載入的對象將需要更新或者重新產生載入的類如果該類被重新產生那麼自定義的東西就會丟失
  回頭再來看看主動代碼產生的好處
  在這裡我們可以看到在運行時使用reflection是不可以接受的主動的代碼產生有著reflection的全部好處但是沒有它的限制還可以繼續使用reflection不過只是在代碼的產生過程而不是運行的過程理由如下
  更少冒險運行時的reflection明顯是更冒險的特別是問題變得復雜的時候
  基於單元測試但並不是編譯器
  多功能性產生的代碼有著runtime reflection的全部好處而且有著runtime reflection沒有的好處
  更易懂雖然經過多次的處理但是將遞歸和reflection結合仍然是很復雜的產生源代碼的方式更加容易解釋和理解代碼產生過程需要遞歸和reflection但得到的結果是可查看的源代碼而不是難以理解的東西
  寫代碼產生器
  要寫一個代碼產生器在思考的時候你不能只是簡單地編寫一個方案來解決一個問題你應該看得更遠代碼產生器(以及reflection)需要你作更多的思考如果你只是使用runtime reflection你就不得不在運行時概念化問題而不是使用簡單兼容性好的源代碼來解決問題代碼產生要求你從兩個方面來查看問題代碼產生過程會將抽象的概念轉變為實際的源代碼Runtime reflection則一直是抽象的
  代碼產生過程將你的思考過程轉變為代碼然後產生並且編譯代碼編譯器會讓你知道你的思考過程在語法上是否正確單元測試則可以驗證代碼在運行時的行為就動態特性方面runtime reflection就不能達到這個級別的安全性
  代碼產生器
  在經歷後幾次失敗的設計後我認為最簡單的方法是在載入過程中為每一種需要實例化的類產生一個方法一個方法工廠產生每個特別類的正確方法一個代碼編譯對象緩沖來自代碼工廠的方法請求以產生最終源代碼文件的內容
  MethodCode對象是代碼產生過程的核心以下就是一個int的代碼產生對象的例子
  public class MethodForInt extends MethodCode {
  private final static MethodParameter param = new MethodParameter(SimpleFileIteratorclass parser);
  public MethodForInt(Class type CodeBuilder builder){
  super(type builder);
  }
  public MethodParameter[] getInputParameters(){
  return new MethodParameter[]{
  param
  };
  }
From:http://tw.wingwit.com/Article/program/Java/hx/201311/25959.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.