成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

小例子背后的大道理:Adapter模式詳解

開發 架構
前文說到一位用戶拿著業界標準開關(一個標準的StandardSwitcher,它依賴IStandardSwitchable接口才能工作,然而目前我們的燈并不支持這個接口)出現在我面前,叫囂著他的“標準開關”應該能打開我們的燈。

前文說到一位用戶拿著業界標準開關(一個標準的StandardSwitcher,它依賴IStandardSwitchable接口才能工作,然而目前我們的燈并不支持這個接口)出現在我面前,叫囂著他的“標準開關”應該能打開我們的燈。好吧,這個需求是合理的,的確應該支持。但是該死的是,為什么沒有早一點兒知道這個標準的存在呢?這樣就不需要花費時間和人力定義這個接口,現在也不會這么糾結。和上次一樣,先講故事、演進方案,再分析背后的思想。

這回主要講解Adapter模式,GoF講解了這個模式是什么,怎么用,用在什么地方。我想來解釋一下Adapter模式的要點是什么,對Adapter模式的延展,以及對Adapter模式的誤用。順便得瑟一下我對面向對象設計的理解。

兩個方案

現在有兩個選擇。

  1. 讓我們的燈直接支持標準開關。也就是讓燈實現IStandardSwitchable接口。

    • 好處:成本低,實現方式優雅。

    • 壞處:相當于放棄了已經買了我們的燈,又想用標準開關的用戶。

  2. 不改變現在的燈,讓標準開關能打開我們的燈。標準接口我們改不了,燈也不能改。好在計算機界有句話,叫“加一層可以解決一切問題”。這讓我想到了買外國電器附贈的那個電源接口轉換器。現在,我們的燈需要個類似的玩意兒。

    • 好處:支持所有的燈。

    • 壞處:這東西都是要附贈的,會降低我們的利潤。

     第一個方案很簡單,就是讓Light多實現個接口就OK了。圖就不給了。

現在分析第二個方案,標準接口依賴IStandardSwitchable接口,那我們必須有一個類來實現它,并完成所需要的功能——操作燈。咱也是學過設計模式的人,這個問題很明顯可以用Adapter模式來解釋。

相關類圖很容易就可以畫出來。

clip_image002

圖1 讓燈支持IStandardSwitchable接口的方案 

其對應的代碼會是這個樣子:

  1. public interface IStandardSwitchable  
  2. {  
  3.     void TurnOn();  
  4.     void TurnOff();  
  5. }  
  6.  
  7.  
  8. public class SwitcherAdapter : IStandardSwitchable  
  9. {  
  10.     public Light Switchee { get; set; }  
  11.  
  12.  
  13.     public void TurnOn()  
  14.     {  
  15.         Switchee.TurnOn();  
  16.     }  
  17.  
  18.  
  19.     public void TurnOff()  
  20.     {  
  21.         Switchee.TurnOff();  
  22.     }  

代碼1

Job Done。Light通過SwitcherAdapter支持了新的接口,這簡直就是應用適配器模式的典范啊。(嗯,這句的確是反話,不過你猜出來為什么這個Adapter不屬于適配器模式嗎?)

“上回真是白跟你說了那么多,平時沒覺得你這么不開竅啊。你自己好好想想吧!”背后看著我畫UML圖的設計Guru好像有點兒生氣。

上回?我冷靜下來回想上回的內容和現在的問題。上回講的DIP,講不要依賴實現,要依賴抽象。再想想目前的需求,我們有燈,有收音機,如果用戶說要用標準開關開收音機,難道還要實現一個RadioAdapter不成?這顯然違反了OCP。

需求是要“通過加一層讓燈支持標準開關”,但是并不是說這一層就要使用燈,為了讓這個Adapter更加通用,應該讓Adapter依賴ISwitchable接口。像下面這個樣子。

clip_image004

圖2 Adapter模式 

與代碼1的差別,僅僅是SwitcherAdapter里的Switchee屬性的類型改成ISwitchable而已。代碼就不再貼了。其所體現的原則就是上一篇講的DIP。

這個事兒其實任何人靜靜地想想都能想到。但我繞這個彎子,其實是想順便表達這樣一個意思:一個緊急需求來了的時候,人們更容易傾向于把完成工作放在第一位,從而一時忽視了設計的嚴謹度,事后又忘了重構,于是Bad Smell就這樣產生了。當然,這些大家也都知道。

面向對象的設計并不是對現實的模擬

(這一節算是一個插曲吧,因為這個論點太大,寫出來都覺得不自量力,不寫又覺得對不起自己愛得瑟的作風。一點拙見,大家多多批評。覺得偏題太遠的話可以直接看一下節。)

但是(重點來了),為什么緊張時做出的直觀設計更可能是錯誤的呢?因為人一緊張就容易憑感覺,而使用直覺做設計時,大都會以現實世界為原本,但是良好的面向對象設計,是絕對不能僅僅依靠現實世界的。其實圖1 的設計從直覺上來講是符合需求,也很符合人們對這個世界的認知的。但是它并不是一個良好的面向對象設計。圖2是相對良好的設計,但是圖2顯然又沒有圖1 那么直觀,那么好理解,那么符合這個世界的真實狀態

圖2和圖1 的差別僅僅在于Adapter要依賴誰上,Adapter要依賴于ISwitchable接口這個事兒,并不是為了更真實地模擬這個世界,而純粹地是為了解耦合而出現(或者說,為了依賴抽象)。但是在現實世界中,是不存在解不解耦合的概念的。解耦合是為了保證設計上的靈活性引入的概念。

現實中事物間的依賴都是具體的是為了復用、靈活性等才引入的抽象,客觀現實是不存在抽象的。抽象是要取決于你是如何看待客觀事物的。舉個例子,在動物學家看來,人與動物間有IS-A關系;但是如果你是要開發一款MMORPG游戲,人(NPC和Avatar)和動物(一般會是怪物)應該是不會有IS-A關系的。觀察的角度不同,就會得出不同的設計;這些設計沒有對錯之分,只有是否滿足需求之別。

所以,有些地方,把面向對象的設計過程解釋為對現實世界的模擬是很片面的。如果僅僅以現實世界的樣子對系統進行設計,得出的設計很可能是僵化的,就像圖1那樣。(有人可能想說我曲解了人家的意思,但是我想說,你寫成那個樣子明明就是故意給人誤解的,至少是很容易引導人誤解。容易被誤解,就是有問題。沒什么好狡辯的。)

但是,這并不意味著做設計就要全面地抽象,模擬現實世界的好處是代碼容易理解,但是如果全部抽象成圖2那樣,所有都抽象出個接口,所有都依賴抽象,那代碼的可讀性顯然會下降。所以,好的面向對象設計,會是真實地模擬現實與抽象現實間的取舍的過程。如果你看過一些功能相似、但實現不同的開源框架,會發現有些好理解,有些不好理解,其根本原因就是其抽象的層次或者說抽象的程度不一樣。抽象度過高,靈活性也許上去了,但是并不見得就是好事兒。過度設計,就是因為對現實的抽象度太高,造成可讀性差,不好維護,還沒解決問題,就先被問題解決掉了。

上面的例子可能依然沒有什么說服力。我再舉一個。上篇文章有人回復說,

“開關里面還包含一個開關接口 ,很奇怪的方式。 

在我看來應該是燈光有開關”。

我想感謝一下這位朋友,因為他提出的這個思路,我一開始就潛意識地無視掉了。經他一提,我才意識這也是設計過程中一類常見的問題。這個設計是一個很真實地反應現實的設計。但是并不是一個可行的類設計。如果你按這個方案寫代碼,就會發現很多問題。原因我已經回復了。

總結一下,做面向對象設計的時候,請記得自己要做的是什么?不要讓現實世界的“真實”的樣子混淆了視聽。面向對象設計,是以可復用地、靈活地實現需求為目標的,對現實的抽象,而不是對現實的模擬;抽象的結果很可能在現實中并不存在。

Adapter模式的關鍵

Adapter模式最關鍵的要求是:Adapter是對兩個功能相近的接口間的適配。如果被適配的對象是個具體類,那么多數情況下,Adapter非但不會帶來好處,反而是僅僅增加了維護成本,就像前面說的,有一個新的具體類出現,就要同時添加一個Adapter。

(如果你非說你見過很多 “適配”具體類的,你是對的,但是那叫Proxy,不叫Adapter,解決的也不是同一種問題,而且多數情況下,Proxy是可以自動生成的,所以不需要擔心加一個類,就要自己實現一個對應的Proxy的問題。可以用下面這個圖對比一下,來自《敏捷軟件開發》)

clip_image006

圖3 Proxy模式 

這不是在死摳Adapter模式的含義。因為只有理解Adapter的目標、適用范圍之后,才不會誤用這個模式。見過不少人理解力很好或是英文很好,看到 Adapter這個詞是個模式就想當然地覺得自己“知道”了這個模式的用法(畢竟這個模式也的確不復雜),并“用”了起來。比如圖1的那個例子,就是最常見的誤用之一。

這也不是在死摳名詞。給模式命名的好處之一就是讓兩個都懂模式的人溝通起來更順暢。模式名所表達的,不是一個簡單的類關系圖,而是對要解決問題的類型的定位和解決問題的策略。

Adapter,表示遇到的問題是接口不匹配。

Proxy,表示遇到的問題是主體邏輯與附加邏輯(持久化、網絡傳輸等)糾纏。

名詞用錯了,就可能會帶來不必要的誤會。

如果你就是覺得沒必要死摳概念,下面的“廣義Adapter模式”可能會比較適合你。

廣義Adapter模式

這年頭好像什么東西都非要搞出個狹義和廣義之分。我個人比較反感這一點,因為狹廣之分的存在,本身就是一種對概念的模糊。這導致人們在溝通時,如果遇到問題,常常要想一下對方說的是廣義的還是狹義的,而不是把焦點放在問題本身。這像是給自己和對方找借口或是后路。或許是因為大家都想給自己留個后路,這東西才會這么流行。附經典對白一則:

“嗯?不對吧,不應該是XXXX嗎?”

“呃,我說的是一種廣義上的XXXX。”

“哦。(Shit!)”

每個人們學習模式,總會有自己的理解,自己的抽象。當理解的角度不同的時候,就會把Adapter模式的內涵延展到不同的地方。這就導致了不同人對廣義Adapter的定義是不同的。

比如《敏捷軟件開發》,從邏輯關系出發,把Adapter的概念延展為:使用一個特定的類,實現對方法調用的定向派發(我自己總結的,原文沒這話)。從這個概念上講,Adapter模式可以用于對具體類的適配。因為這個延展的概念實際上已經超出了原有的GoF的定義。這顯然不能說是錯誤的,你甚至會覺得這個人水平真高,能對設計模式進行再抽象,再擴展。

但是問題是,不同人對同一概念的延展方向是不同的。你覺得Adapter和Delegate/Event有什么相似之處嗎?我相信更多人會覺得Observer模式與Delegate/Event的相似之處更多些。因為無數的人和書都說過C#的Delegate /Event機制就是Observer模式的一種具體實現。如果你面試的時候說,Delegation就是一種Adapter,你的面試可能就直接 Pass了。這事兒也的確真實地發生過

但是如果去看《Pro Objective-C Design Pattern for iOS》第112頁,對Adapter的描述真的是這樣的。

“The Delegation pattern was once one of the inspirations for cataloging the Adapter

pattern in the “Gang of Four” book.

如果你怕我斷章取義,可以自己去看。

這個人是從類與類之間的關系出發,把具有相似結構、交互方式的類的組合都定義為Adapter。你說他的理解錯了嗎?我只能說:“狹義來講,是錯的,廣義來講,是對的。”但這是這個世界上最操蛋的答案之一。

像上面鏈接的博客里描述的那個面試者,顯然就成了廣義與狹義之分的犧牲品——他說的是廣義的Adapter,但是面試官想聽到的是狹義的Adapter。(不過從后面的敘述來看,那個面試官也是半瓶子醋,問Delegate的時候居然會順便問異步,讓我不得不懷疑他是不是認為事件是異步觸發的。)

對Adapter有獨特的理解很好,能把Adapter, Observer, Delegation, Proxy全統一起來理解更是NB。但是,其實在多數情況下,越是獨到的見解,越可能會給面對面的溝通帶來障礙。這些獨到的見解在個人頓悟模式的過程中很有用,寫到書里也很好,畢竟讀者可以細細體味,幫助讀者從不同的角度思考問題;但想在面試之類的當面溝通的場合上裝逼,然后自己的口才又不咋地。怕只會畫虎不成反類犬。

對Adapter模式的誤用

學歷史的時候,常常見到“左派”、“右派”這樣的詞,意思是他們走的路線不對。這個詞用得很形象,都是走極端。 模式的誤用,常見的誤用之一也是走極端。

圖2 的Adapter模式,成功的把標準的開關接口適配到了我們的接口上。于是便有了一個順理成章的思路,ISwitchable和 IStandardSwitchable接口都是對開關的定義,我們通過Adapter模式,讓支持IStandardSwitchable的開關能夠開我們的燈。

那么我們之前的這個設計:

clip_image008

圖4. 第一回中提出的開關開燈方案(Abstract Server)

 

是不是應該改成這樣?

clip_image010

圖5. 試圖把Adapter模式用于實現DIP 

這個設計相比原來的設計方案,抽象度更高、耦合性更低,Light甚至不需要依賴ISwitchable接口就可以工作,這樣我們可以很有信心地說,我們可以讓一切類都支持ISwitchable接口!

這個想法很豐滿,但是現實很骨感。如果你認真看過了前面的內容,應該已經知道這個方案其實很爛的原因了。

這個世界很微妙,《敏捷軟件開發》(P370)的確就把圖5稱為Adapter模式,不過你應該懂的,他說的是廣義的Adapter模式。并不是說對具體類的Adapter就一定是誤用,如果沒有違反OCP就不是誤用,如果那個Light是個Utility類,就不算是誤用。

(如果你想噴Adapter模式本來就有兩種,一種是基于類的,一種是基于對象的,你最好先去把Adapter概念回個爐,我們說的根本不是一碼事兒。)

誤用的原因

     我自己總結了一下出現這種誤用的原因有三(這些原因會讓人出現各種形式的誤用,而不針對Adapter模式):

  1. 想當然地類推。像上面那樣,從適配IStandardSwitchable可行,直接推出適配ISwitchable也可以,畢竟這是同樣功能的接口啊。但是,不能這樣類推。

  2. 妄圖用同一個方式解決所有問題的想法或創造出一個work for everything的東西的想法。我直覺上就想用熱力學第二定律來反駁這想法(和work forever差不多意思),不過“no silver bullet”可能更合適些。但是有些人,尤其是Level越高的,就越容易陷入這個泥潭。可能他們覺得不創造些NB的東西出來,就太對不起大家了。當然,這個想法是很好的,但是也要講求方法,拿著錘子就看什么都是釘子的做法是要不得的。 參考十條不錯的編程觀點第一條就是,獨立思考,妄圖通過學習各種模式就可以應對一切設計問題的想法就是要不得的。還有一條讓我印象很深的就是關于Google的使用,推薦大家也去看看。

  3. 對設計原則和設計模式的理解不透徹。如果真正理解了Adapter模式的意圖、適用范圍。是不會犯這樣的錯誤的。但是很可惜,這個世界上的誘惑太多了,哪怕Wikipedia這樣看似很權威的地方都在誤導著別人(所以,自己思考,自己判斷)。Wikipedia上對DIP的解釋是這樣的:“Applying the dependency inversion principle can also be seen as applying the Adapter pattern, i.e.”直譯過來就是“遵循依賴倒置原則可被視同于應用適配器模式”。Oops…用了適配器模式,那的確是DIP了,但是適配器并不用來達到DIP這個目標的,適配器模式雖然DIP,但是如果用來現實DIP,效果卻很糟糕,帶來了更多 的問題。我猜作者的本意只是想表達:適配器模式本身是符合DIP原則的。這沒錯。但是我相信有一票人看到這里就去研究適配器模式并計劃用它來實現DIP 了。(有人嫌我啰嗦,我只是想把問題說清楚,讓更多的人無可誤解。)

    這里說的缺乏經驗可能并不是工作年限不足的問題,更可能的是態度的問題,要么是對Adapter模式想當然、覺得自己在字面上的理解就差不多,要么是想對Adapter模式進行所謂的“活用”,結果犯了激進冒險主義錯誤。

下回預告

我們的燈賣得好,用戶就多了起來,需求也多了起來。這樣一下子來了兩個用戶,一個要求,我要用兩個開關控制同一個燈(床頭一個,走廊一個,看來這用戶晚上常起夜);另一個要求,我想用一個開關控制屋子里所有的燈(看來這用戶不差錢)。

那么,我們又需要做出怎樣的設計來應對這些需求呢?

原文鏈接:http://www.cnblogs.com/nankezhishi/archive/2012/05/31/2529201.html

責任編輯:林師授 來源: 博客園
相關推薦

2012-08-20 09:35:37

DIP接口

2016-11-15 13:52:19

2018-05-10 16:21:19

產品

2017-11-29 12:56:02

人工智能大數據成語

2016-05-09 10:38:36

樣本量選擇

2020-02-14 14:05:10

刪庫跑路發生

2021-04-07 14:45:56

軟件測試編程

2012-08-02 10:46:34

JavaAdapter模式

2009-04-29 09:06:18

C#設計模式Adapter

2024-07-31 10:41:16

C#設計模式

2012-02-07 13:31:14

SpringJava

2015-10-20 15:00:51

七牛云

2011-05-27 14:16:18

Android 體系

2021-07-15 10:11:56

IT流程設計流程流程文化

2016-11-22 19:54:56

點擊率預估推薦算法廣告

2021-08-04 16:50:22

數字化

2012-05-17 09:43:53

實力

2009-05-26 15:15:17

ITSM運維管理摩卡

2010-01-21 09:08:53

.NET設計模式

2018-10-29 08:47:48

傳輸模式無線
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品国产91久久综合桃花 | 色婷婷国产精品综合在线观看 | 免费在线性爱视频 | 亚洲视频在线一区 | 精品区一区二区 | 日韩高清一区 | 亚洲精品二区 | 国产一区二区在线免费观看 | 天天天操 | 欧美精品一区在线发布 | 日韩在线免费看 | 婷婷久久精品一区二区 | 久久国产精品久久久久久 | 国产小视频在线 | 在线视频成人 | 色综合一区 | 免费观看一级毛片视频 | 国产视频线观看永久免费 | 精品国产一区二区国模嫣然 | 韩日一区二区三区 | 久久久高清 | 日韩国产精品一区二区三区 | 天天干视频 | 在线观看国产三级 | 国产精品国产三级国产aⅴ中文 | 夜夜艹 | 91视频.| 国产激情一区二区三区 | av手机在线免费观看 | 在线一区 | 亚洲区在线 | 在线观看黄色大片 | 亚洲日本激情 | 欧美一级久久 | 欧美一区二区三区四区视频 | 日韩中文一区 | 国产在线精品一区二区 | 99综合网 | 久久人爽 | 亚洲成人精品一区二区 | 羞羞视频在线观看免费观看 |