菜鳥(niǎo)版JAVA設(shè)計(jì)模式—適配器模式,裝飾模式乡小,代理模式異同
一阔加、概念
適配器模式,允許因?yàn)榻涌诓患嫒荻荒茉谝黄鸸ぷ鞯念惞ぷ髟谝黄鹇樱龇ㄊ菍㈩愖约旱慕涌诎谝粋€(gè)已存在的類中胜榔。
裝飾器模式,原有的不能滿足現(xiàn)有的需求零远,對(duì)原有的進(jìn)行增強(qiáng)苗分。
代理模式,同一個(gè)類而去調(diào)用另一個(gè)類的方法牵辣,不對(duì)這個(gè)方法進(jìn)行直接操作摔癣。
適配器的特點(diǎn)在于兼容,從代碼上的特點(diǎn)來(lái)說(shuō),適配類與原有的類具有相同的接口择浊,并且持有新的目標(biāo)對(duì)象戴卜。就如同一個(gè)三孔轉(zhuǎn)2孔的適配器一樣,他有三孔的插頭琢岩,可以插到三孔插座里投剥,又有兩孔的插座可以被2孔插頭插入。適配器模式是在于對(duì)原有3孔的改造担孔。在使用適配器模式的時(shí)候江锨,我們必須同時(shí)持有原對(duì)象,適配對(duì)象糕篇,目標(biāo)對(duì)象啄育。。拌消。挑豌。
裝飾器模式特點(diǎn)在于增強(qiáng),他的特點(diǎn)是被裝飾類和所有的裝飾類必須實(shí)現(xiàn)同一個(gè)接口墩崩,而且必須持有被裝飾的對(duì)象氓英,可以無(wú)限裝飾。
代理模式的特點(diǎn)在于隔離鹦筹,隔離調(diào)用類和被調(diào)用類的關(guān)系铝阐,通過(guò)一個(gè)代理類去調(diào)用。
總的來(lái)說(shuō)就是如下三句話:
- 適配器模式是將一個(gè)類(a)通過(guò)某種方式轉(zhuǎn)換成另一個(gè)類(b).
- 裝飾模式是在一個(gè)原有類(a)的基礎(chǔ)之上增加了某些新的功能變成另一個(gè)類(b).
- 代理模式是將一個(gè)類(a)轉(zhuǎn)換成具體的操作類(b).
二盛龄、例子
先簡(jiǎn)單介紹一下饰迹,公司有一個(gè)ORDER系統(tǒng),專門用于提供訂單管理的接口余舶,提供給O2O商城啊鸭,WAP商城,手機(jī)類APP匿值,微信等客戶端調(diào)用赠制。ORDER系統(tǒng)在上線之時(shí),已經(jīng)包含了非常完整的數(shù)據(jù)操作挟憔。
現(xiàn)手機(jī)類APP需要升級(jí)钟些,老的接口可能不能滿足需求,如原O2O訂單提貨延長(zhǎng)有效期接口需要提供截至?xí)r間和訂單號(hào)绊谭,新APP可能不提供截至?xí)r間政恍,只需要提供訂單號(hào)即可延長(zhǎng)時(shí)間。而老的接口又不能修改达传,因?yàn)槔系膐rder接口是針對(duì)所有平臺(tái)的篙耗,那么必須將新接口要求與原接口進(jìn)行適配迫筑。。宗弯。
解決方案如下:新增AppAdepter類脯燃,用來(lái)適配老的接口,同時(shí)開(kāi)發(fā)給新的接口蒙保。辕棚。。邓厕。
先給出原接口和實(shí)現(xiàn)類
/**
* 原接口逝嚎,需要傳入orderId,時(shí)間
*
*/
public interface SourceOrderApi {
public void updateDate(String orderId,String date,String client);
}
public class SourceOrderApiImpl implements SourceOrderApi{
@Override
public void updateDate(String orderId, String date, String client) {
System.out.println(client+"已將訂單"+orderId+"的有效期延長(zhǎng)至"+date);
}
}
public class Test {
public static void main(String[] args) {
SourceOrderApi sourceOrderApi = new SourceOrderApiImpl();
sourceOrderApi.updateDate("123456", "2014-10-15", "user");
}
}
//運(yùn)行結(jié)果:user已將訂單123456的有效期延長(zhǎng)至2014-10-15
新的接口和實(shí)現(xiàn)類:
public interface AppOrderApi {
//只需要傳入訂單Id即可
public void updateDate(String orderId,String client);
}
public class AppOrderApiImpl implements AppOrderApi{
SourceOrderApi sourceOrderApi;
public AppOrderApiImpl(){
sourceOrderApi = new SourceOrderApiImpl();
}
@Override
public void updateDate(String orderId,String client) {
//這里適配的方式隨意邑狸,但是保證是要完全兼容原有的懈糯,就是保證調(diào)用原有的接口
sourceOrderApi.updateDate(orderId, "9999-12-31",client);
}
}
public class Test {
public static void main(String[] args) {
AppOrderApi appOrderApi = new AppOrderApiImpl();
appOrderApi.updateDate("123456", "user");
}
}
//運(yùn)行結(jié)果:user已將訂單123456的有效期延長(zhǎng)至9999-12-31
在這套代碼中,新的實(shí)現(xiàn)類持有了老接口的對(duì)象单雾,就是把這個(gè)對(duì)象new 出來(lái)。她紫。硅堆。然后在新的方法里,進(jìn)行適配操作贿讹。而這里所謂的適配就是兼容老接口和兼容新接口渐逃。兼容老接口非常簡(jiǎn)單,就是直接調(diào)用老的方法即可民褂。而兼容新接口就是你所要去做的業(yè)務(wù)邏輯茄菊。。赊堪。比如我這里做了比較簡(jiǎn)單的操作面殖,app類的統(tǒng)一傳入“9999-12-31”,這些都是根據(jù)具體的項(xiàng)目需求去實(shí)現(xiàn)你的業(yè)務(wù)代碼哭廉〖沽牛看到這,很多人會(huì)覺(jué)得 這樣的代碼是不是很常見(jiàn)遵绰。辽幌。。椿访。我自己都特意去翻了一下我之前的代碼乌企,很多類似的寫(xiě)法。成玫。加酵。其實(shí)設(shè)計(jì)模式很多時(shí)候都融入到我們的代碼中端辱,只是我們沒(méi)有去理解這種模式,所以就算你自己寫(xiě)出來(lái)了虽画,都不知道這是一種適配器模式舞蔽。。码撰。
那么再回頭來(lái)說(shuō)代理模式渗柿,在代碼上,和適配器模式有著相似的地方脖岛!
再比如朵栖,現(xiàn)在我們公司的系統(tǒng)又要進(jìn)行升級(jí),我們?cè)鹊慕涌跊](méi)有加入安全機(jī)制柴梆,導(dǎo)致了任何人都可以隨意調(diào)用這個(gè)接口陨溅,現(xiàn)在公司需要對(duì)這個(gè)接口進(jìn)行改造,只其只能被admin這個(gè)客戶端調(diào)用绍在,其他用戶一律要輸入賬號(hào)密碼才能調(diào)用门扇。那么上面原接口和類不需要改動(dòng),我們只需要新增代理器即可偿渡。臼寄。。
這里就會(huì)顯出代理模式和適配器模式最大的區(qū)別溜宽,代理模式是與原對(duì)象實(shí)現(xiàn)同一個(gè)接口吉拳,而適配器類則是匹配新接口,說(shuō)白了适揉,實(shí)現(xiàn)一個(gè)新的接口留攒。
public class ProxySourceOrderApiImpl implements SourceOrderApi {
SourceOrderApi sourceOrderApiImpl;
public ProxySourceOrderApiImpl(){
sourceOrderApiImpl = new SourceOrderApiImpl();
}
@Override
public void updateDate(String orderId, String date, String client) {
//進(jìn)行判斷,如果是admin則更新否則讓其輸入賬號(hào)密碼
if("admin".equals(client)){
sourceOrderApiImpl.updateDate(orderId, date, client);
}else{
System.out.println("賬號(hào)不是admin嫉嘀,沒(méi)有查詢權(quán)限炼邀,請(qǐng)輸入以admin操作");
}
}
}
這里的代理類必須要持要實(shí)現(xiàn)原接口和持有原接口的對(duì)象,才能稱之為代理類吃沪。
這樣汤善,我們不需要修改原先的實(shí)現(xiàn)類,用一個(gè)代理類來(lái)進(jìn)行過(guò)濾票彪。有些人可能有這樣的疑惑红淡,為什么不直接在原實(shí)現(xiàn)類中修改,記住JAVA設(shè)計(jì)模式的基本原則降铸,對(duì)內(nèi)關(guān)閉修改在旱。
假如你并沒(méi)有對(duì)方的類,別人只是提供給你這樣一個(gè)可以操作的JAR包你如何去做推掸?把JAR包反編譯桶蝎,再修改驻仅?又假如某些地方可以直接調(diào)用原有接口,你如果修改原有的實(shí)現(xiàn)類登渣,豈不是對(duì)其他的地方也造成了影響噪服?不能修改原有代碼這是基本原則。
最后胜茧,就是裝飾器模式粘优,裝飾器模式與相對(duì)于上面2種模式更好理解,差別也最大呻顽。
假如說(shuō)雹顺,現(xiàn)在這個(gè)延長(zhǎng)訂單,不光可以延長(zhǎng)訂單提貨有效期廊遍,可以延長(zhǎng)訂單的退貨有效期嬉愧。
這就是一個(gè)典型的裝飾器,你需要做的是豐富原接口的功能喉前,并且不改動(dòng)原先的接口没酣。
public class NewSourceOrderApiImpl implements SourceOrderApi {
SourceOrderApi sourceOrderApi;
public NewSourceOrderApiImpl(SourceOrderApi sourceOrderApi){
this.sourceOrderApi = sourceOrderApi;
}
@Override
public void updateDate(String orderId, String date, String client) {
sourceOrderApi.updateDate(orderId, date, client);
System.out.println(client+"已將訂單"+orderId+"的退款期延長(zhǎng)至"+date);
}
}
在裝飾器模式中,必須要有被裝飾的類和裝飾的類被饿。四康。在這套代碼中,原先SourceOrderApi的對(duì)象就是被裝飾的類狭握,而新建NewSourceOrderApiImpl 就是裝飾類,裝飾類必須把被裝飾的對(duì)象當(dāng)作參數(shù)傳入疯溺。
這就是和代理模式的代碼不同之處论颅,代理模式一定是自身持有這個(gè)對(duì)象,不需要從外部傳入囱嫩。而裝飾模式的一定是從外部傳入恃疯,并且可以沒(méi)有順序,按照代碼的實(shí)際需求隨意挑換順序墨闲,就如你吃火鍋先放白菜還是先放丸子都可以今妄。
再?gòu)氖褂蒙蟻?lái)看,代理模式注重的是隔離限制鸳碧,讓外部不能訪問(wèn)你實(shí)際的調(diào)用對(duì)象盾鳞,比如權(quán)限控制,裝飾模式注重的是功能的拓展瞻离,在同一個(gè)方法下實(shí)現(xiàn)更多的功能腾仅。