IAdaptable和IAdaptableFactory
Posted on 2006-10-09 19:08 Dart 閱讀(1825) 評(píng)論(3) 編輯 收藏 所屬分類(lèi): Eclipse
1.?簡(jiǎn)介和簡(jiǎn)單的實(shí)現(xiàn)
IAdapteable實(shí)際上在Eclipse早期版本中不叫這個(gè)名字,它原來(lái)的名字叫做IExtensible,顧名思義就是可以擴(kuò)展的意思,后來(lái)為了更能突出是由一個(gè)類(lèi)配適到一個(gè)接口這么一種機(jī)制,所以改名為IAdaptable。
這個(gè)接口有什么用呢,其實(shí)說(shuō)白了,就是提供一個(gè)類(lèi)型的轉(zhuǎn)換機(jī)制。比如下面這段代碼:
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類(lèi)繼承了ArrayList,并且實(shí)現(xiàn)了IAdaptable接口,我們想要將它轉(zhuǎn)化成Vector類(lèi)型對(duì)象,于是在getAdapter方法中我們判斷傳入?yún)?shù)類(lèi)型,如果是Vector類(lèi)那么就新生成一個(gè)Vector對(duì)象,將ArrayList中的值全部賦給它,并返回。
這樣,我們就可以寫(xiě)出以下代碼:
??Vector?v? = ?(Vector)?list.getAdapter(Vector. class );
ArrayList會(huì)返回Vector對(duì)象,這個(gè)對(duì)象是ArrayList的一個(gè)另外一種類(lèi)型的副本。
2.一個(gè)Swing程序
讀者會(huì)問(wèn):這有什么用啊,不就簡(jiǎn)單轉(zhuǎn)化一下麼。其實(shí)說(shuō)實(shí)話,從上面的代碼來(lái)看確實(shí)沒(méi)什么用,但是如果我們換一個(gè)場(chǎng)景試試。
寫(xiě)這么一個(gè)Swing程序:有一個(gè)對(duì)話框,其中它有一個(gè)ComboBox和一個(gè)Table,ComboBox中存放的是一個(gè)名為Person類(lèi)型的對(duì)象,當(dāng)ComboBox的選項(xiàng)發(fā)生改變的時(shí)候,就在Table上顯示它的屬性,我們假設(shè)這個(gè)Swing程序已經(jīng)在某個(gè)項(xiàng)目中開(kāi)始實(shí)施,并且其界面布局不易更改。
看看代碼:
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類(lèi)的部分代碼:
???????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);
??????????????????????}
??????????????????});
??????}
?運(yùn)行我們的代碼,會(huì)發(fā)現(xiàn)效果還可以,每當(dāng)我們選項(xiàng)改變的時(shí)候,Table就如同一個(gè)屬性欄一樣,改變著自己的內(nèi)容:
3.需求變更
OK,問(wèn)題來(lái)了。我寫(xiě)完這段代碼后,組長(zhǎng)告訴我,現(xiàn)在我們有一個(gè)新的需求,就是Combox中不僅僅有Person類(lèi)型存在,而且還有一些貨物(Product)類(lèi)型,也就是說(shuō),我的table顯示屬性不能光針對(duì)Person這個(gè)類(lèi)型了,還需要顯示Product的屬性。
我心里罵了句:早TMD干嘛了,都快交活兒了才告訴我。
無(wú)奈,我新增加了一個(gè)Product類(lèi)型,然后更改了ActionListener中的部分代碼:
?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);
結(jié)果還是讓人滿意的:
后來(lái)我感覺(jué)ActionListener代碼有一些凌亂,又封裝了一個(gè)Builder類(lèi),讓它創(chuàng)建TableModel:
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;
}
我對(duì)自己的代碼還算滿意,至少目前能用了。
4.需求又變了
第二天,組長(zhǎng)告訴我,需求又變了,這會(huì)不但多增加一個(gè)“服裝”類(lèi)型,Product類(lèi)型屬性顯示有錯(cuò)誤,并且需要增加一個(gè)Tree,顯示當(dāng)前同種類(lèi)型直接的層次結(jié)構(gòu),等等。
我聽(tīng)了領(lǐng)導(dǎo)嘮叨半個(gè)小時(shí)后,打開(kāi)了我剛寫(xiě)的Builder類(lèi),往里面增加著我的代碼……
類(lèi)圖大致如下:
程序經(jīng)過(guò)修改后,好不容易又符合要求了,情況又發(fā)生了變化,組長(zhǎng)需要我繼續(xù)修改。我無(wú)奈地看著組長(zhǎng),組長(zhǎng)也無(wú)奈地看著我那用if-else堆成的代碼……
“悲哀,真讓我替你感到悲~哀!”組長(zhǎng)操著本山的腔調(diào)這樣對(duì)我說(shuō)。
是啊,多悲哀啊,一個(gè)設(shè)計(jì)上的錯(cuò)誤讓我的代碼無(wú)法適應(yīng)需求的變化。
好了,讓我們回到IAdaptable上。
通過(guò)上面的例子,我看可以發(fā)現(xiàn)這么一個(gè)情況:同樣一個(gè)對(duì)象,在程序里面往往有許多不同的顯示方式(不僅僅是在UI顯示,在其他一些代碼里,需要轉(zhuǎn)化成另外類(lèi)型或者數(shù)據(jù)結(jié)構(gòu))。
如果我用IAdapteable的思想來(lái)實(shí)現(xiàn)剛才的Swing屬性顯示,會(huì)怎么樣呢?
重新寫(xiě)一遍ActionListener中的代碼:
Object?obj? = ?comboBox.getSelectedItem();
TableModel?jTable1Model? = ? null ;
if (obj? instanceof ?IAdaptable){
???????jTable1Model? = ?(TableModel)?((IAdaptable)obj).getAdapter(TableModel. class );
}
table.setModel(jTable1Model);
然后分別讓Person和Product實(shí)現(xiàn)IAdaptable接口:
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 ;
?}
}
其實(shí)我們的代碼量并沒(méi)有任何的改變,前后都是一樣的。
但是我們將Table需要顯示的模型(TableModel),現(xiàn)在是作為擴(kuò)展類(lèi)接口抽取了出來(lái),而那些需要在Table上顯示自己屬性的業(yè)務(wù)模型(Person,Product)實(shí)現(xiàn)了IAdaptable接口,將顯示模型(TableModel)作為了自己的擴(kuò)展接口類(lèi)型給予實(shí)例返回,并且UI代碼中,Table和業(yè)務(wù)模型之間形成一種契約:凡是實(shí)現(xiàn)了IAdaptable的接口才可以獲得在該Table上顯示的資格,并且Table從IAdaptable的getAdapter方法獲得顯示模型:
這樣一來(lái),我們的Swing程序不僅功能能夠?qū)崿F(xiàn),而且UI部分代碼和業(yè)務(wù)模型代碼之間的耦合性減小了。
而且,如果需求發(fā)生變化,比如像剛才提到那樣“需要增加一個(gè)Tree,顯示當(dāng)前同種類(lèi)型直接的層次結(jié)構(gòu)”,那我們就在getAdaper方法中返回一個(gè)TreeModel的副本,然后在UI中增加一個(gè)Tree,讓它像Table一樣,從IAdaptable接口中取出我們的TreeModel即可——UI擴(kuò)展也變得容易起來(lái)。
現(xiàn)在我可以對(duì)組長(zhǎng)說(shuō):讓需求變化來(lái)得更猛烈些吧!
5.模型代碼無(wú)法修改
有這樣一個(gè)問(wèn)題:如果我們的模型已經(jīng)存在,而且代碼已經(jīng)無(wú)法修改了怎么辦?
IAdapterFactory就是為這種情況準(zhǔn)備的。
先看看IAdapterFactory:
? public ?Object?getAdapter(Object?adapter,Class?clazz);
}
這里面的方法和IAdaptable差不多,只是多了一個(gè)參數(shù),這個(gè)參數(shù)就是需要我們返回Adapter接口的對(duì)象。
在Eclipse中IAdapterFactory并不是單獨(dú)存在的,而是有一個(gè)IAdapterManager對(duì)它進(jìn)行維護(hù)的:
? public ?Object?getAdapter(Object?adapter,Class?clazz);
? public ? boolean ?registerAdapters?(Class?clazz,IAdaptableFactory?factory);
}
現(xiàn)在讓我們這樣來(lái)修改剛才的Swing程序:
假設(shè)Product類(lèi)型是第三方提供的jar包,我們已經(jīng)無(wú)法修改它的代碼了,那我們就需要用到IAdapableFactory的擴(kuò)展方法。請(qǐng)看下面的代碼
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;
?}
}
有了這兩個(gè)實(shí)現(xiàn)類(lèi)后,我們?cè)偃バ薷囊幌翧ctionListener中的代碼:
?????????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);
好了,只要我們?cè)谶m當(dāng)?shù)牡胤剑瑢AdaptableFactory注冊(cè)進(jìn)IAdaptaerManager,那我們對(duì)無(wú)法修改代碼的業(yè)務(wù)模型也能進(jìn)行接口的擴(kuò)展了。
6.結(jié)束語(yǔ)
在Eclipse中,IAdaptable的應(yīng)用非常廣泛,而且如果實(shí)現(xiàn)了IAdaptable接口的類(lèi)被成為Platform Object,可見(jiàn)IAdaptable在Eclipse框架中的分量。本人的知識(shí)有限,如果有遺漏或者錯(cuò)誤的地方,還請(qǐng)各位讀者指出。