Xiaobo Sun

          Eclipse-Unix http://umlfact.berlios.de/~s_xsun/

          IAdaptable & IAdapterFactory

          在Eclipse中使用IAdaptable接口的方式有兩種
          1:某個類希望提供新的接口,但又不希望將其暴露在API中,在這種情況下,IAdaptable接口中的方法getAdaptor()方法將由本類實現。(希望支持新的接口,而又不想把已經發布的API造成影響,這種機制很有用)
          2:外界要求某個類提供新的服務,這種情況下不需要修改現有類的代碼,getAdaptor()由一個工廠提供。(不使用decorator模式的原因是在以后的對比中會出現兩個含有相同接口的類)
          創建適配器工廠(IAdapterFactory),注冊到適配器管理器(IAdapterManager),
          代碼如下:IadapterFactory factory = new AdapterFactory();
          IadapterManager manager = Platform.getAdapterManager();
          Manager.registerAdapters(factory,Ifile.class);

          適配對象需要實現platformobject
          public Object getAdapter(Class adapter)
          {
                     return InternalPlatform.getDefault().getAdapterManager().getAdapter(this, adapter);
               }
          若未實現,則需要下列調用:Platform.getAdapterManager().getAdapter(this,adapter)



          (Quote from http://www.aygfsteel.com/reloadcn/archive/2006/10/09/74153.aspx)

          1. 簡介和簡單的實現

          IAdapteable實際上在Eclipse早期版本中不叫這個名字,它原來的名字叫做IExtensible,顧名思義就是可以擴展的意思,后來為了更能突出是由一個類配適到一個接口這么一種機制,所以改名為IAdaptable。
          這個接口有什么用呢,其實說白了,就是提供一個類型的轉換機制。比如下面這段代碼:

          Class IAdaptable  
          public   interface
           IAdaptable  {
           
          public
           Object getAdapter(Class clazz);
          }

          Class ListAdapter
          public   class  ListAdapter  extends  ArrayList  implements
           IAdaptable 

          {
           
          public
           Object getAdapter(Class clazz) {
            
          if (clazz  ==  Vector. class
          ){
             Vector v 
          =   new  Vector( this
          .size());
             v.addAll(
          this
          );
             
          return
           v;
            }
            
          return   null
          ;
           }
          }


          ListAdapter 類繼承了ArrayList,并且實現了IAdaptable接口,我們想要將它轉化成Vector類型對象,于是在getAdapter方法中我們判斷 傳入參數類型,如果是Vector類那么就新生成一個Vector對象,將ArrayList中的值全部賦給它,并返回。

          這樣,我們就可以寫出以下代碼:

          ListAdapter list  =   new  ListAdapter();
            Vector v 
          =  (Vector) list.getAdapter(Vector. class );


          ArrayList會返回Vector對象,這個對象是ArrayList的一個另外一種類型的副本。

          2.一個Swing程序

          讀者會問:這有什么用啊,不就簡單轉化一下麼。其實說實話,從上面的代碼來看確實沒什么用,但是如果我們換一個場景試試。

          寫 這么一個Swing程序:有一個對話框,其中它有一個ComboBox和一個Table,ComboBox中存放的是一個名為Person類型的對象,當 ComboBox的選項發生改變的時候,就在Table上顯示它的屬性,我們假設這個Swing程序已經在某個項目中開始實施,并且其界面布局不易更改。

          看看代碼:

          Class person
          public   class
           Person {
           
          private  String name  =   " name "
          ;
           
          private  String age  =   " 23 "
          ;
           
          private  String sex  =   " male "
          ;
           
           
          public
           Person(String name){
            
          this
          .setName(name);
           }
           
           
          public
           String getName() {
            
          return
           name;
           }
           
           
          public   void
           setName(String name) {
            
          this .name  =
           name;
           }
           ……
          }

          UI類的部分代碼:

          {
                 table 
          =   new
           JTable();
                 
          this
          .getContentPane().add(table);
                 table.setBounds(
          218 2 171 248
          );
                }
                {
                 ComboBoxModel jComboBox1Model 
          =   new
           DefaultComboBoxModel(
                  
          new  Object[] {  new  Person( " rEloaD " ),  new  Person( " b "
          ) });
                 comboBox 
          =   new
           JComboBox();
                 
          this
          .getContentPane().add(comboBox);
                 comboBox.setModel(jComboBox1Model); 
                 comboBox.addActionListener(
          new
           ActionListener(){
                           
          public   void
           actionPerformed(ActionEvent e){
                               JComboBox comboBox 
          =
          (JComboBox)e.getSource();
                               Person p 
          =
           (Person)comboBox.getSelectedItem();
                               TableModel jTable1Model 
          =   new
           DefaultTableModel(
                                               
          new  String[][] { {  " Name "
          , p.getName() },
                                                                 { 
          " Sex "
          , p.getSex() },
                                                                { 
          " Age "
          , p.getAge() }},
                                               
          new  String[] {  " Column 1 " " Column 2 "
           });
                               table.setModel(jTable1Model);
                                }
                            });

                }





           運行我們的代碼,會發現效果還可以,每當我們選項改變的時候,Table就如同一個屬性欄一樣,改變著自己的內容:



          3.需求變更

          OK,問題來了。我寫完這段代碼后,組長告訴我,現在我們有一個新的需求,就是Combox中不僅僅有Person類型存在,而且還有一些貨物(Product)類型,也就是說,我的table顯示屬性不能光針對Person這個類型了,還需要顯示Product的屬性。

          我心里罵了句:早TMD干嘛了,都快交活兒了才告訴我。

          無奈,我新增加了一個Product類型,然后更改了ActionListener中的部分代碼:

          JComboBox comboBox  = (JComboBox)e.getSource();
           Object obj 
          =
           comboBox.getSelectedItem();
           TableModel jTable1Model 
          =   null
          ;
            
          if (obj  instanceof
           Person){
                jTable1Model 
          =   new
           DefaultTableModel(
                                    
          new  String[][] { {  " Name "
          , ((Person)obj).getName() },
                                                     { 
          " Sex "
          , ((Person)obj).getSex() },
                                                      { 
          " Age "
          , ((Person)obj).getAge() }},
                                     
          new  String[] {  " Column 1 " " Column 2 "
           });
            }
            
          if (obj  instanceof
           Product){
                jTable1Model 
          =   new
           DefaultTableModel(
                                  
          new  String[][] { {  " Name "
          , ((Product)obj).name },
                                                    { 
          " price "
          , ((Product)obj).price },
                                                    { 
          " quantity "
          , ((Product)obj).quantity }},
                                   
          new  String[] {  " Column 1 " " Column 2 "
           });

            }
            table.setModel(jTable1Model);




          結果還是讓人滿意的:



          后來我感覺ActionListener代碼有一些凌亂,又封裝了一個Builder類,讓它創建TableModel:

          public   static  TableModel modelBuilder(Object obj){
          TableModel jTable1Model = null;
            
          if (obj  instanceof
           Person){
                    jTable1Model 
          =   new
           DefaultTableModel(
                       
          new  String[][] { {  " Name "
          , ((Person)obj).getName() },
                         { 
          " Sex "
          , ((Person)obj).getSex() },
                         { 
          " Age "
          , ((Person)obj).getAge() }},
                       
          new  String[] {  " Column 1 " " Column 2 "
           });
                                    }
                                    
          if (obj  instanceof
           Product){
                                      jTable1Model 
          =   new
           DefaultTableModel(
                         
          new  String[][] { {  " Name "
          , ((Product)obj).name },
                           { 
          " price "
          , ((Product)obj).price },
                           { 
          " quantity "
          , ((Product)obj).quantity }},
                         
          new  String[] {  " Column 1 " " Column 2 "
           });

            }
          return
           jTable1Model;

          }


          我對自己的代碼還算滿意,至少目前能用了。

          4.需求又變了

          第二天,組長告訴我,需求又變了,這會不但多增加一個“服裝”類型,Product類型屬性顯示有錯誤,并且需要增加一個Tree,顯示當前同種類型直接的層次結構,等等。

          我聽了領導嘮叨半個小時后,打開了我剛寫的Builder類,往里面增加著我的代碼……

          類圖大致如下:


           

          程序經過修改后,好不容易又符合要求了,情況又發生了變化,組長需要我繼續修改。我無奈地看著組長,組長也無奈地看著我那用if-else堆成的代碼……

          “悲哀,真讓我替你感到悲~哀!”組長操著本山的腔調這樣對我說。

          是啊,多悲哀啊,一個設計上的錯誤讓我的代碼無法適應需求的變化。



          好了,讓我們回到IAdaptable上。

          通過上面的例子,我看可以發現這么一個情況:同樣一個對象,在程序里面往往有許多不同的顯示方式(不僅僅是在UI顯示,在其他一些代碼里,需要轉化成另外類型或者數據結構)。

          如果我用IAdapteable的思想來實現剛才的Swing屬性顯示,會怎么樣呢?

          重新寫一遍ActionListener中的代碼:

          JComboBox comboBox  = (JComboBox)e.getSource();
          Object obj 
          =
           comboBox.getSelectedItem();
          TableModel jTable1Model 
          =   null
          ;
          if (obj  instanceof
           IAdaptable){
                 jTable1Model 
          =  (TableModel) ((IAdaptable)obj).getAdapter(TableModel. class
          );
          }
          table.setModel(jTable1Model);

          然后分別讓Person和Product實現IAdaptable接口:

          Class Person:
          public   class  Person  implements
           IAdaptable{
             …..
             
          public
           Object getAdapter(Class clazz) {
            
          if (clazz  ==  TableModel. class
          ){
              
          return   new
           DefaultTableModel(
                
          new  String[][] { {  " Name "
          , getName() },
                  { 
          " Sex "
          , getSex() },
                  { 
          " Age "
          , getAge() }},
                
          new  String[] {  " Column 1 " " Column 2 "
           });
            }
            
          return   null
          ;
           }
          }

          Class Product
          public   class  Product  implements
           IAdaptable{
           ……
              
          public
           Object getAdapter(Class clazz) {
            
          if (clazz  ==  TableModel. class
          ){
              
          return   new
           DefaultTableModel(
                
          new  String[][] { {  " Name "
          , getName() },
                  { 
          " Sex "
          , getSex() },
                  { 
          " Age "
          , getAge() }},
                
          new  String[] {  " Column 1 " " Column 2 "
           });
            }
            
          return   null
          ;
           }
          }

          其實我們的代碼量并沒有任何的改變,前后都是一樣的。

          但 是我們將Table需要顯示的模型(TableModel),現在是作為擴展類接口抽取了出來,而那些需要在Table上顯示自己屬性的業務模型 (Person,Product)實現了IAdaptable接口,將顯示模型(TableModel)作為了自己的擴展接口類型給予實例返回,并且UI 代碼中,Table和業務模型之間形成一種契約:凡是實現了IAdaptable的接口才可以獲得在該Table上顯示的資格,并且Table從 IAdaptable的getAdapter方法獲得顯示模型:




          這樣一來,我們的Swing程序不僅功能能夠實現,而且UI部分代碼和業務模型代碼之間的耦合性減小了。

          而 且,如果需求發生變化,比如像剛才提到那樣“需要增加一個Tree,顯示當前同種類型直接的層次結構”,那我們就在getAdaper方法中返回一個 TreeModel的副本,然后在UI中增加一個Tree,讓它像Table一樣,從IAdaptable接口中取出我們的TreeModel即可—— UI擴展也變得容易起來。

          現在我可以對組長說:讓需求變化來得更猛烈些吧!


          5.模型代碼無法修改

          有這樣一個問題:如果我們的模型已經存在,而且代碼已經無法修改了怎么辦?

          IAdapterFactory就是為這種情況準備的。

          先看看IAdapterFactory:

          public   interface  IAdaptableFactory {
           
          public
           Object getAdapter(Object adapter,Class clazz);
          }


          這里面的方法和IAdaptable差不多,只是多了一個參數,這個參數就是需要我們返回Adapter接口的對象。

          在Eclipse中IAdapterFactory并不是單獨存在的,而是有一個IAdapterManager對它進行維護的:

          public   interface  IAdaptableManager {
           
          public
           Object getAdapter(Object adapter,Class clazz);
           
          public   boolean
           registerAdapters (Class clazz,IAdaptableFactory factory);
          }


          現在讓我們這樣來修改剛才的Swing程序:

          假設Product類型是第三方提供的jar包,我們已經無法修改它的代碼了,那我們就需要用到IAdapableFactory的擴展方法。請看下面的代碼

          Class AdaptableFactoryImpl
          public   class  AdaptableFactoryImpl  implements
           IAdaptableFactory {
           
          public
           Object getAdapter(Object adapter, Class clazz) {
            
          if (adapter  instanceof
           Product){
             
          if (clazz  == TableModel. class
          ){
              
          return   new
           DefaultTableModel(
                
          new  String[][] { {  " Name "
          ,((Product)adapter).name },
                  { 
          " price "
          , ((Product)adapter).price },
                  { 
          " quantity "
          , ((Product)adapter).quantity }},
                
          new  String[] {  " Column 1 " " Column 2 "
           });
             }
            }
            
          return   null
          ;
           }
           
          public
           Class[] getAdapterList() {
            
          return   new  Class[]{TableModel. class
          };
           }
          }

          Class AdapterManagerImpl:
          public   class  AdapterManagerImpl  implements
           IAdaptableManager {
           
          private   static  AdapterManagerImpl instance  =   null
          ;
           
          private  Hashtable table  =   new
           Hashtable();
           
           
          private
           AdapterManagerImpl(){}
           
           
          public
           Object getAdapter(Object adapter, Class clazz) {
            Object factory 
          =
           table.get(adapter.getClass());
            
          if (factory  !=   null
          ){
             
          return
           ((IAdaptableFactory)factory).getAdapter(adapter,clazz);
            }
            
          return   null
          ;
           }

           
          public   boolean
           registerFacotry(Class clazz, IAdaptableFactory factory) {
            
          try
          {
             table.put(clazz,factory);
             
          return   true
          ;
            }
          catch
          (Exception e){
             
          return   false
          ;
            }
           }
           
          public   synchronized   static
           AdapterManagerImpl getInstance() {
            
          if (instance  ==   null ) instance  =   new
           AdapterManagerImpl();
            
          return
           instance;
           }
          }


          有了這兩個實現類后,我們再去修改一下ActionListener中的代碼:

                   JComboBox comboBox  =  (JComboBox) e.getSource();
                   Object obj 
          =
           comboBox.getSelectedItem();
                   TableModel jTable1Model 
          =   null
          ;
                   
          if  (obj  instanceof
           IAdaptable) {
                    jTable1Model 
          =
           (TableModel) ((IAdaptable) obj)
                      .getAdapter(TableModel.
          class
          );
                   } 
          else
           {
                    jTable1Model 
          =
           (TableModel) AdapterManagerImpl
                      .getInstance().getAdapter(obj,
                        TableModel.
          class
          );
                   }
                   table.setModel(jTable1Model);


          好了,只要我們在適當的地方,將IAdaptableFactory注冊進IAdaptaerManager,那我們對無法修改代碼的業務模型也能進行接口的擴展了。

          6.結束語

          在Eclipse中,IAdaptable的應用非常廣泛,而且如果實現了IAdaptable接口的類被成為Platform Object,可見IAdaptable在Eclipse框架中的分量。本人的知識有限,如果有遺漏或者錯誤的地方,還請各位讀者指出。



          posted on 2007-06-12 11:06 Xiaobo Sun 閱讀(648) 評論(0)  編輯  收藏 所屬分類: EclipseDesign Pattern


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2007年6月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          1234567

          導航

          統計

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 临沭县| 武义县| 刚察县| 福鼎市| 太原市| 汉川市| 内江市| 遂川县| 稻城县| 海淀区| 隆尧县| 抚远县| 常宁市| 邛崃市| 鹿泉市| 珲春市| 南城县| 抚远县| 鄂温| 津南区| 九龙城区| 大港区| 河池市| 衢州市| 安西县| 防城港市| 义乌市| 新竹市| 五莲县| 平谷区| 普安县| 石城县| 清涧县| 元氏县| 铅山县| 临清市| 忻城县| 东兴市| 卫辉市| 平远县| 墨脱县|