月蝕傳說

          浮躁讓人失去理智
          posts - 25, comments - 101, trackbacks - 0, articles - 0
            BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

          IAdaptable和IAdaptableFactory

          Posted on 2006-10-09 19:08 Dart 閱讀(1825) 評論(3)  編輯  收藏 所屬分類: Eclipse

          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框架中的分量。本人的知識有限,如果有遺漏或者錯誤的地方,還請各位讀者指出。


          評論

          # re: IAdaptable和IAdaptableFactory  回復  更多評論   

          2007-03-27 16:53 by iceman
          寫得清楚,謝謝

          # re: IAdaptable和IAdaptableFactory  回復  更多評論   

          2007-05-17 11:40 by re
          看不到圖片了

          # re: IAdaptable和IAdaptableFactory  回復  更多評論   

          2011-03-03 08:48 by d
          寫得很好的。
          主站蜘蛛池模板: 太谷县| 镇沅| 汝阳县| 德阳市| 麻江县| 修武县| 宜丰县| 荃湾区| 云阳县| 保德县| 湖北省| 江油市| 房产| 介休市| 江源县| 武夷山市| 津市市| 桂阳县| 横峰县| 舒兰市| 凤台县| 抚松县| 西安市| 东乡族自治县| 洞头县| 鲁甸县| 依安县| 平凉市| 清远市| 洮南市| 怀集县| 墨脱县| 新昌县| 麻栗坡县| 博兴县| 敦煌市| 油尖旺区| 搜索| 睢宁县| 含山县| 拜泉县|