程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> RTTI有害嗎

RTTI有害嗎

編輯:關於JAVA

本章的各種設計方案都在努力避免使用RTTI,這或許會給大家留下“RTTI有害”的印象(還記得可憐的goto嗎,由於給人印象不佳,根本就沒有放到Java裡來)。但實際情況並非絕對如此。正確地說,應該是RTTI使用不當才“有害”。我們之所以想避免RTTI的使用,是由於它的錯誤運用會造成擴展性受到損害。而我們事前提出的目標就是能向系統自由加入新類型,同時保證對周圍的代碼造成盡可能小的影響。由於RTTI常被濫用(讓它查找系統中的每一種類型),會造成代碼的擴展能力大打折扣——添加一種新類型時,必須找出使用了RTTI的所有代碼。即使僅遺漏了其中的一個,也不能從編譯器那裡得到任何幫助。
然而,RTTI本身並不會自動產生非擴展性的代碼。讓我們再來看一看前面提到的垃圾回收例子。這一次准備引入一種新工具,我把它叫作TypeMap。其中包含了一個Hashtable(散列表),其中容納了多個Vector,但接口非常簡單:可以添加(add())一個新對象,可以獲得(get())一個Vector,其中包含了屬於某種特定類型的所有對象。對於這個包含的散列表,它的關鍵在於對應的Vector裡的類型。這種設計方案的優點(根據Larry O'Brien的建議)是在遇到一種新類型的時候,TypeMap會動態加入一種新類型。所以不管什麼時候,只要將一種新類型加入系統(即使在運行期間添加),它也會正確無誤地得以接受。
我們的例子同樣建立在c16.Trash這個“包”(Package)內的Trash類型結構的基礎上(而且那兒使用的Trash.dat文件可以照搬到這裡來)。

 

//: DynaTrash.java 
// Using a Hashtable of Vectors and RTTI
// to automatically sort trash into
// vectors. This solution, despite the
// use of RTTI, is extensible.
package c16.dynatrash;
import c16.trash.*;
import java.util.*;

// Generic TypeMap works in any situation:
class TypeMap {
  private Hashtable t = new Hashtable();
  public void add(Object o) {
    Class type = o.getClass();
    if(t.containsKey(type))
      ((Vector)t.get(type)).addElement(o);
    else {
      Vector v = new Vector();
      v.addElement(o);
      t.put(type,v);
    }
  }
  public Vector get(Class type) {
    return (Vector)t.get(type);
  }
  public Enumeration keys() { return t.keys(); }
  // Returns handle to adapter class to allow
  // callbacks from ParseTrash.fillBin():
  public Fillable filler() { 
    // Anonymous inner class:
    return new Fillable() {
      public void addTrash(Trash t) { add(t); }
    };
  }
}

public class DynaTrash {
  public static void main(String[] args) {
    TypeMap bin = new TypeMap();
    ParseTrash.fillBin("Trash.dat",bin.filler());
    Enumeration keys = bin.keys();
    while(keys.hasMoreElements())
      Trash.sumValue(
        bin.get((Class)keys.nextElement()));
  }
} ///:~


盡管功能很強,但對TypeMap的定義是非常簡單的。它只是包含了一個散列表,同時add()負擔了大部分的工作。添加一個新類型時,那種類型的Class對象的句柄會被提取出來。隨後,利用這個句柄判斷容納了那類對象的一個Vector是否已存在於散列表中。如答案是肯定的,就提取出那個Vector,並將對象加入其中;反之,就將Class對象及新Vector作為一個“鍵-值”對加入。
利用keys(),可以得到對所有Class對象的一個“枚舉”(Enumeration),而且可用get(),可通過Class對象獲取對應的Vector。
filler()方法非常有趣,因為它利用了ParseTrash.fillBin()的設計——不僅能嘗試填充一個Vector,也能用它的addTrash()方法試著填充實現了Fillable(可填充)接口的任何東西。filter()需要做的全部事情就是將一個句柄返回給實現了Fillable的一個接口,然後將這個句柄作為參數傳遞給fillBin(),就象下面這樣:
ParseTrash.fillBin("Trash.dat", bin.filler());
為產生這個句柄,我們采用了一個“匿名內部類”(已在第7章講述)。由於根本不需要用一個已命名的類來實現Fillable,只需要屬於那個類的一個對象的句柄即可,所以這裡使用匿名內部類是非常恰當的。
對這個設計,要注意的一個地方是盡管沒有設計成對歸類加以控制,但在fillBin()每次進行歸類的時候,都會將一個Trash對象插入bin。
通過前面那些例子的學習,DynaTrash類的大多數部分都應當非常熟悉了。這一次,我們不再將新的Trash對象置入類型Vector的一個bin內。由於bin的類型為TypeMap,所以將垃圾(Trash)丟進垃圾筒(Bin)的時候,TypeMap的內部歸類機制會立即進行適當的分類。在TypeMap裡遍歷並對每個獨立的Vector進行操作,這是一件相當簡單的事情:

 

    Enumeration keys = bin.keys();
    while(keys.hasMoreElements())
      Trash.sumValue(
        bin.get((Class)keys.nextElement()));

就象大家看到的那樣,新類型向系統的加入根本不會影響到這些代碼,亦不會影響TypeMap中的代碼。這顯然是解決問題最圓滿的方案。盡管它確實嚴重依賴RTTI,但請注意散列表中的每個鍵-值對都只查找一種類型。除此以外,在我們增加一種新類型的時候,不會陷入“忘記”向系統加入正確代碼的尴尬境地,因為根本就沒有什麼代碼需要添加。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved