自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

開發(fā) 架構(gòu)
前文說到一位用戶拿著業(yè)界標(biāo)準(zhǔn)開關(guān)(一個(gè)標(biāo)準(zhǔn)的StandardSwitcher,它依賴IStandardSwitchable接口才能工作,然而目前我們的燈并不支持這個(gè)接口)出現(xiàn)在我面前,叫囂著他的“標(biāo)準(zhǔn)開關(guān)”應(yīng)該能打開我們的燈。

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

這回主要講解Adapter模式,GoF講解了這個(gè)模式是什么,怎么用,用在什么地方。我想來(lái)解釋一下Adapter模式的要點(diǎn)是什么,對(duì)Adapter模式的延展,以及對(duì)Adapter模式的誤用。順便得瑟一下我對(duì)面向?qū)ο笤O(shè)計(jì)的理解。

兩個(gè)方案

現(xiàn)在有兩個(gè)選擇。

  1. 讓我們的燈直接支持標(biāo)準(zhǔn)開關(guān)。也就是讓燈實(shí)現(xiàn)IStandardSwitchable接口。

    • 好處:成本低,實(shí)現(xiàn)方式優(yōu)雅。

    • 壞處:相當(dāng)于放棄了已經(jīng)買了我們的燈,又想用標(biāo)準(zhǔn)開關(guān)的用戶。

  2. 不改變現(xiàn)在的燈,讓標(biāo)準(zhǔn)開關(guān)能打開我們的燈。標(biāo)準(zhǔn)接口我們改不了,燈也不能改。好在計(jì)算機(jī)界有句話,叫“加一層可以解決一切問題”。這讓我想到了買外國(guó)電器附贈(zèng)的那個(gè)電源接口轉(zhuǎn)換器?,F(xiàn)在,我們的燈需要個(gè)類似的玩意兒。

    • 好處:支持所有的燈。

    • 壞處:這東西都是要附贈(zèng)的,會(huì)降低我們的利潤(rùn)。

     第一個(gè)方案很簡(jiǎn)單,就是讓Light多實(shí)現(xiàn)個(gè)接口就OK了。圖就不給了。

現(xiàn)在分析第二個(gè)方案,標(biāo)準(zhǔn)接口依賴IStandardSwitchable接口,那我們必須有一個(gè)類來(lái)實(shí)現(xiàn)它,并完成所需要的功能——操作燈。咱也是學(xué)過設(shè)計(jì)模式的人,這個(gè)問題很明顯可以用Adapter模式來(lái)解釋。

相關(guān)類圖很容易就可以畫出來(lái)。

clip_image002

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

其對(duì)應(yīng)的代碼會(huì)是這個(gè)樣子:

  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支持了新的接口,這簡(jiǎn)直就是應(yīng)用適配器模式的典范啊。(嗯,這句的確是反話,不過你猜出來(lái)為什么這個(gè)Adapter不屬于適配器模式嗎?)

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

上回?我冷靜下來(lái)回想上回的內(nèi)容和現(xiàn)在的問題。上回講的DIP,講不要依賴實(shí)現(xiàn),要依賴抽象。再想想目前的需求,我們有燈,有收音機(jī),如果用戶說要用標(biāo)準(zhǔn)開關(guān)開收音機(jī),難道還要實(shí)現(xiàn)一個(gè)RadioAdapter不成?這顯然違反了OCP。

需求是要“通過加一層讓燈支持標(biāo)準(zhǔn)開關(guān)”,但是并不是說這一層就要使用燈,為了讓這個(gè)Adapter更加通用,應(yīng)該讓Adapter依賴ISwitchable接口。像下面這個(gè)樣子。

clip_image004

圖2 Adapter模式 

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

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

面向?qū)ο蟮脑O(shè)計(jì)并不是對(duì)現(xiàn)實(shí)的模擬

(這一節(jié)算是一個(gè)插曲吧,因?yàn)檫@個(gè)論點(diǎn)太大,寫出來(lái)都覺得不自量力,不寫又覺得對(duì)不起自己愛得瑟的作風(fēng)。一點(diǎn)拙見,大家多多批評(píng)。覺得偏題太遠(yuǎn)的話可以直接看一下節(jié)。)

但是(重點(diǎn)來(lái)了),為什么緊張時(shí)做出的直觀設(shè)計(jì)更可能是錯(cuò)誤的呢?因?yàn)槿艘痪o張就容易憑感覺,而使用直覺做設(shè)計(jì)時(shí),大都會(huì)以現(xiàn)實(shí)世界為原本,但是良好的面向?qū)ο笤O(shè)計(jì),是絕對(duì)不能僅僅依靠現(xiàn)實(shí)世界的。其實(shí)圖1 的設(shè)計(jì)從直覺上來(lái)講是符合需求,也很符合人們對(duì)這個(gè)世界的認(rèn)知的。但是它并不是一個(gè)良好的面向?qū)ο笤O(shè)計(jì)。圖2是相對(duì)良好的設(shè)計(jì),但是圖2顯然又沒有圖1 那么直觀,那么好理解,那么符合這個(gè)世界的真實(shí)狀態(tài)

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

現(xiàn)實(shí)中事物間的依賴都是具體的是為了復(fù)用、靈活性等才引入的抽象,客觀現(xiàn)實(shí)是不存在抽象的。抽象是要取決于你是如何看待客觀事物的。舉個(gè)例子,在動(dòng)物學(xué)家看來(lái),人與動(dòng)物間有IS-A關(guān)系;但是如果你是要開發(fā)一款MMORPG游戲,人(NPC和Avatar)和動(dòng)物(一般會(huì)是怪物)應(yīng)該是不會(huì)有IS-A關(guān)系的。觀察的角度不同,就會(huì)得出不同的設(shè)計(jì);這些設(shè)計(jì)沒有對(duì)錯(cuò)之分,只有是否滿足需求之別。

所以,有些地方,把面向?qū)ο蟮脑O(shè)計(jì)過程解釋為對(duì)現(xiàn)實(shí)世界的模擬是很片面的。如果僅僅以現(xiàn)實(shí)世界的樣子對(duì)系統(tǒng)進(jìn)行設(shè)計(jì),得出的設(shè)計(jì)很可能是僵化的,就像圖1那樣。(有人可能想說我曲解了人家的意思,但是我想說,你寫成那個(gè)樣子明明就是故意給人誤解的,至少是很容易引導(dǎo)人誤解。容易被誤解,就是有問題。沒什么好狡辯的。)

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

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

“開關(guān)里面還包含一個(gè)開關(guān)接口 ,很奇怪的方式。 

在我看來(lái)應(yīng)該是燈光有開關(guān)”。

我想感謝一下這位朋友,因?yàn)樗岢龅倪@個(gè)思路,我一開始就潛意識(shí)地?zé)o視掉了。經(jīng)他一提,我才意識(shí)這也是設(shè)計(jì)過程中一類常見的問題。這個(gè)設(shè)計(jì)是一個(gè)很真實(shí)地反應(yīng)現(xiàn)實(shí)的設(shè)計(jì)。但是并不是一個(gè)可行的類設(shè)計(jì)。如果你按這個(gè)方案寫代碼,就會(huì)發(fā)現(xiàn)很多問題。原因我已經(jīng)回復(fù)了。

總結(jié)一下,做面向?qū)ο笤O(shè)計(jì)的時(shí)候,請(qǐng)記得自己要做的是什么?不要讓現(xiàn)實(shí)世界的“真實(shí)”的樣子混淆了視聽。面向?qū)ο笤O(shè)計(jì),是以可復(fù)用地、靈活地實(shí)現(xiàn)需求為目標(biāo)的,對(duì)現(xiàn)實(shí)的抽象,而不是對(duì)現(xiàn)實(shí)的模擬;抽象的結(jié)果很可能在現(xiàn)實(shí)中并不存在。

Adapter模式的關(guān)鍵

Adapter模式最關(guān)鍵的要求是:Adapter是對(duì)兩個(gè)功能相近的接口間的適配。如果被適配的對(duì)象是個(gè)具體類,那么多數(shù)情況下,Adapter非但不會(huì)帶來(lái)好處,反而是僅僅增加了維護(hù)成本,就像前面說的,有一個(gè)新的具體類出現(xiàn),就要同時(shí)添加一個(gè)Adapter。

(如果你非說你見過很多 “適配”具體類的,你是對(duì)的,但是那叫Proxy,不叫Adapter,解決的也不是同一種問題,而且多數(shù)情況下,Proxy是可以自動(dòng)生成的,所以不需要擔(dān)心加一個(gè)類,就要自己實(shí)現(xiàn)一個(gè)對(duì)應(yīng)的Proxy的問題??梢杂孟旅孢@個(gè)圖對(duì)比一下,來(lái)自《敏捷軟件開發(fā)》)

clip_image006

圖3 Proxy模式 

這不是在死摳Adapter模式的含義。因?yàn)橹挥欣斫釧dapter的目標(biāo)、適用范圍之后,才不會(huì)誤用這個(gè)模式。見過不少人理解力很好或是英文很好,看到 Adapter這個(gè)詞是個(gè)模式就想當(dāng)然地覺得自己“知道”了這個(gè)模式的用法(畢竟這個(gè)模式也的確不復(fù)雜),并“用”了起來(lái)。比如圖1的那個(gè)例子,就是最常見的誤用之一。

這也不是在死摳名詞。給模式命名的好處之一就是讓兩個(gè)都懂模式的人溝通起來(lái)更順暢。模式名所表達(dá)的,不是一個(gè)簡(jiǎn)單的類關(guān)系圖,而是對(duì)要解決問題的類型的定位和解決問題的策略。

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

Proxy,表示遇到的問題是主體邏輯與附加邏輯(持久化、網(wǎng)絡(luò)傳輸?shù)龋┘m纏。

名詞用錯(cuò)了,就可能會(huì)帶來(lái)不必要的誤會(huì)。

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

廣義Adapter模式

這年頭好像什么東西都非要搞出個(gè)狹義和廣義之分。我個(gè)人比較反感這一點(diǎn),因?yàn)楠M廣之分的存在,本身就是一種對(duì)概念的模糊。這導(dǎo)致人們?cè)跍贤〞r(shí),如果遇到問題,常常要想一下對(duì)方說的是廣義的還是狹義的,而不是把焦點(diǎn)放在問題本身。這像是給自己和對(duì)方找借口或是后路。或許是因?yàn)榇蠹叶枷虢o自己留個(gè)后路,這東西才會(huì)這么流行。附經(jīng)典對(duì)白一則:

“嗯?不對(duì)吧,不應(yīng)該是XXXX嗎?”

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

“哦。(Shit!)”

每個(gè)人們學(xué)習(xí)模式,總會(huì)有自己的理解,自己的抽象。當(dāng)理解的角度不同的時(shí)候,就會(huì)把Adapter模式的內(nèi)涵延展到不同的地方。這就導(dǎo)致了不同人對(duì)廣義Adapter的定義是不同的。

比如《敏捷軟件開發(fā)》,從邏輯關(guān)系出發(fā),把Adapter的概念延展為:使用一個(gè)特定的類,實(shí)現(xiàn)對(duì)方法調(diào)用的定向派發(fā)(我自己總結(jié)的,原文沒這話)。從這個(gè)概念上講,Adapter模式可以用于對(duì)具體類的適配。因?yàn)檫@個(gè)延展的概念實(shí)際上已經(jīng)超出了原有的GoF的定義。這顯然不能說是錯(cuò)誤的,你甚至?xí)X得這個(gè)人水平真高,能對(duì)設(shè)計(jì)模式進(jìn)行再抽象,再擴(kuò)展。

但是問題是,不同人對(duì)同一概念的延展方向是不同的。你覺得Adapter和Delegate/Event有什么相似之處嗎?我相信更多人會(huì)覺得Observer模式與Delegate/Event的相似之處更多些。因?yàn)闊o(wú)數(shù)的人和書都說過C#的Delegate /Event機(jī)制就是Observer模式的一種具體實(shí)現(xiàn)。如果你面試的時(shí)候說,Delegation就是一種Adapter,你的面試可能就直接 Pass了。這事兒也的確真實(shí)地發(fā)生過。

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

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

pattern in the “Gang of Four” book.

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

這個(gè)人是從類與類之間的關(guān)系出發(fā),把具有相似結(jié)構(gòu)、交互方式的類的組合都定義為Adapter。你說他的理解錯(cuò)了嗎?我只能說:“狹義來(lái)講,是錯(cuò)的,廣義來(lái)講,是對(duì)的。”但這是這個(gè)世界上最操蛋的答案之一。

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

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

對(duì)Adapter模式的誤用

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

圖2 的Adapter模式,成功的把標(biāo)準(zhǔn)的開關(guān)接口適配到了我們的接口上。于是便有了一個(gè)順理成章的思路,ISwitchable和 IStandardSwitchable接口都是對(duì)開關(guān)的定義,我們通過Adapter模式,讓支持IStandardSwitchable的開關(guān)能夠開我們的燈。

那么我們之前的這個(gè)設(shè)計(jì):

clip_image008

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

 

是不是應(yīng)該改成這樣?

clip_image010

圖5. 試圖把Adapter模式用于實(shí)現(xiàn)DIP 

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

這個(gè)想法很豐滿,但是現(xiàn)實(shí)很骨感。如果你認(rèn)真看過了前面的內(nèi)容,應(yīng)該已經(jīng)知道這個(gè)方案其實(shí)很爛的原因了。

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

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

誤用的原因

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

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

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

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

    這里說的缺乏經(jīng)驗(yàn)可能并不是工作年限不足的問題,更可能的是態(tài)度的問題,要么是對(duì)Adapter模式想當(dāng)然、覺得自己在字面上的理解就差不多,要么是想對(duì)Adapter模式進(jìn)行所謂的“活用”,結(jié)果犯了激進(jìn)冒險(xiǎn)主義錯(cuò)誤。

下回預(yù)告

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

那么,我們又需要做出怎樣的設(shè)計(jì)來(lái)應(yīng)對(duì)這些需求呢?

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

責(zé)任編輯:林師授 來(lái)源: 博客園
相關(guān)推薦

2012-08-20 09:35:37

DIP接口

2016-11-15 13:52:19

2018-05-10 16:21:19

產(chǎn)品

2017-11-29 12:56:02

人工智能大數(shù)據(jù)成語(yǔ)

2020-02-14 14:05:10

刪庫(kù)跑路發(fā)生

2016-05-09 10:38:36

樣本量選擇

2021-04-07 14:45:56

軟件測(cè)試編程

2012-08-02 10:46:34

JavaAdapter模式

2009-04-29 09:06:18

C#設(shè)計(jì)模式Adapter

2024-07-31 10:41:16

C#設(shè)計(jì)模式

2012-02-07 13:31:14

SpringJava

2011-05-27 14:16:18

Android 體系

2015-10-20 15:00:51

七牛云

2021-07-15 10:11:56

IT流程設(shè)計(jì)流程流程文化

2016-11-22 19:54:56

點(diǎn)擊率預(yù)估推薦算法廣告

2012-05-17 09:43:53

實(shí)力

2010-01-21 09:08:53

.NET設(shè)計(jì)模式

2021-08-04 16:50:22

數(shù)字化

2018-10-29 08:47:48

傳輸模式無(wú)線

2009-07-08 17:25:05

Java Single
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)