- Live軟件開發(fā)面面談
- 潘俊編著
- 2669字
- 2019-07-30 17:54:59
1.3 如何實(shí)現(xiàn)
關(guān)于針對(duì)接口編程,前面談了是什么(What)和為什么(Why),下面介紹怎么做(How)。下面給出一個(gè)媒體播放器的實(shí)例,實(shí)現(xiàn)利用各種編解碼器來播放媒體文件的功能。第一個(gè)版本的代碼如下。


由于每種編解碼器的解碼方法的名稱均不一樣,所以播放器不僅引用不同編解碼器的實(shí)例,還要了解其API的差異,調(diào)用正確的方法,這樣開發(fā)人員當(dāng)然太痛苦了。首先想到的改進(jìn)方法就是為編解碼器制定統(tǒng)一的接口,之后播放器使用編解碼器時(shí)就方便得多,于是有了第二個(gè)版本的代碼。


第二個(gè)版本的播放器MediaPlayerV2現(xiàn)在的問題就是對(duì)具體編解碼器類型的依賴了。因?yàn)橐蕾噥碓从趯?duì)編解碼器實(shí)例的初始化,所以可將負(fù)責(zé)初始化的代碼從播放器移出,轉(zhuǎn)交另一個(gè)對(duì)象來負(fù)責(zé)。面向?qū)ο箝_發(fā)中的工廠模式(Factory Pattern)常用來創(chuàng)建對(duì)象實(shí)例,我們就先采用它來改進(jìn)播放器。
1.3.1 工廠模式
工廠模式包括抽象工廠(Abstract Factory)和工廠方法(Factory Method)兩種類型。前者可以看作是后者的擴(kuò)展,用于創(chuàng)建一系列相關(guān)的對(duì)象實(shí)例。這里只看工廠方法模式,它又有兩種變體。第一種是創(chuàng)建者基類的工廠方法創(chuàng)建產(chǎn)品基類的實(shí)例,創(chuàng)建者繼承類的工廠方法創(chuàng)建產(chǎn)品繼承類的實(shí)例,一個(gè)創(chuàng)建者繼承類只對(duì)應(yīng)一個(gè)產(chǎn)品繼承類。在這里使用這種模式只會(huì)將對(duì)特定編解碼器類的依賴轉(zhuǎn)移到它對(duì)應(yīng)的特定創(chuàng)建者類上,對(duì)我們的目標(biāo)沒有幫助。另一種變體是創(chuàng)建者類的工廠方法接收參數(shù),以返回不同類型的產(chǎn)品實(shí)例,下面給出這種模式的代碼。

1.3.2 服務(wù)定位器模式
應(yīng)用工廠模式時(shí),返回的對(duì)象都是每次新創(chuàng)建的實(shí)例。如果調(diào)用方并不需要如此,那么該類型對(duì)象的創(chuàng)建成本就太高,每個(gè)實(shí)例占用的資源過大,這種方式很不經(jīng)濟(jì)。又或者調(diào)用方需要每次都獲取到同一個(gè)實(shí)例時(shí),也必須采用其他方式。
可以從另一種角度來描述依賴的問題。一個(gè)對(duì)象需要訪問和調(diào)用其他對(duì)象的屬性和方法,被調(diào)用者可被視為向調(diào)用者提供服務(wù),或者換種說法被調(diào)用者就是服務(wù)(Service)。消除依賴就意味著調(diào)用者能夠以接口的形式獲取服務(wù)。如何獲取服務(wù)呢?花點(diǎn)錢雇一班管家和傭人,或者打電話去家政公司請(qǐng)鐘點(diǎn)工或者有事找相關(guān)機(jī)構(gòu)都可以。翻譯成計(jì)算機(jī)的語言,最自然的思路就是有一個(gè)集中的地方可以根據(jù)所需的接口返回服務(wù)——實(shí)現(xiàn)該接口的對(duì)象。這個(gè)地方按照習(xí)慣被稱為服務(wù)定位器(Service locator),實(shí)現(xiàn)和使用它的編程套路就是服務(wù)定位器模式。
服務(wù)定位器的核心是返回服務(wù)的方法,至于這些服務(wù)對(duì)象本身是怎樣來的,可以根據(jù)實(shí)際情況采用各種方法:預(yù)先創(chuàng)建所有服務(wù),然后添加到定位器內(nèi)部某個(gè)映射數(shù)據(jù)結(jié)構(gòu)內(nèi)保存;調(diào)用者請(qǐng)求服務(wù)時(shí)再創(chuàng)建,并保存以備下次請(qǐng)求;甚至每次請(qǐng)求時(shí)創(chuàng)建。定位器可以是一個(gè)靜態(tài)(Static)類,調(diào)用者直接訪問它的靜態(tài)方法,它容納的服務(wù)也是所有調(diào)用者共享的;也可以是一個(gè)實(shí)例類,調(diào)用者需要訪問它的實(shí)例,服務(wù)只在該實(shí)例的調(diào)用者間共享。前者相當(dāng)于提供公共服務(wù)的家政公司和相關(guān)機(jī)構(gòu),后者類比于私人管家和仆傭。為了簡(jiǎn)便,下列代碼使用了一個(gè)靜態(tài)服務(wù)定位器。


許多情況下,獲取服務(wù)的Resolve <T>方法只需要根據(jù)調(diào)用者提供的接口T返回對(duì)應(yīng)的服務(wù)就可以了,服務(wù)對(duì)象的具體類型是什么由定位器決定,或者更準(zhǔn)確地說由程序的需求和所處的環(huán)境決定。也就是說,調(diào)用者請(qǐng)求的一種接口對(duì)應(yīng)一個(gè)服務(wù)。但是在我們的例子里,媒體播放器需要獲取實(shí)現(xiàn)編解碼器接口的多種具體類型的實(shí)例,所以Resolve <T>方法還補(bǔ)充了一個(gè)可選的字符串參數(shù),用來傳遞編解碼器類型的信息。關(guān)于服務(wù)的來源,這里采用的途徑是通過Register <T>方法添加,同樣有一個(gè)可選的字符串參數(shù)。因?yàn)樘砑臃?wù)是播放器工作之前的準(zhǔn)備,所以將這部分代碼放在一個(gè)單獨(dú)的類ControllerUsingServiceLocatorV1中,再由它來調(diào)用播放器。


1.3.3 依賴注入
上兩種模式有一個(gè)共同點(diǎn),就是無論被調(diào)用者的來源如何,調(diào)用者都要從某個(gè)地方主動(dòng)獲取。我們把這種方式稱為拉。與之相對(duì)的是由外界將調(diào)用者需要的對(duì)象推給它,依賴注入(Dependency injection)就是這樣一種模式。這個(gè)名稱聽上去很深?yuàn)W,其實(shí)本質(zhì)很簡(jiǎn)單。所謂將對(duì)象推給調(diào)用者,在程序中就是指將它作為參數(shù)傳遞給調(diào)用者。根據(jù)一個(gè)對(duì)象接受參數(shù)傳遞的方法的類型和特點(diǎn),專家們又將依賴注入分為好幾種,并給它們都起了很酷的名字。將被調(diào)用者通過調(diào)用者的構(gòu)造函數(shù)傳遞稱為構(gòu)造函數(shù)注入(Constructor injection);通過調(diào)用者的設(shè)值方法或?qū)傩詡鬟f稱為設(shè)值注入(Setter injection);如果用于傳遞的方法是在實(shí)現(xiàn)一個(gè)專為依賴注入而建的接口,就稱為接口注入(Interface injection);如果被調(diào)用者是作為一個(gè)普通方法的參數(shù)傳入,就稱為方法調(diào)用注入(Method call injection)。下列代碼演示了這幾種形式。


構(gòu)造函數(shù)注入、設(shè)值注入和接口注入都將傳入的對(duì)象保存在調(diào)用者的字段里,可供調(diào)用者的所有方法使用。這類情況下,執(zhí)行注入的對(duì)象也就負(fù)責(zé)創(chuàng)建調(diào)用者的實(shí)例,這個(gè)對(duì)象傳統(tǒng)上的稱謂包括容器、應(yīng)用上下文(Application context),它是整個(gè)應(yīng)用程序控制流程的起點(diǎn)。容器提供方法返回注入好了的調(diào)用者實(shí)例,特定于應(yīng)用程序的代碼就從調(diào)用這些方法獲取實(shí)例開始。方法調(diào)用注入傳入的對(duì)象僅僅在該方法內(nèi)被使用,其他方法無法訪問,該方法要使用也只能在每次調(diào)用時(shí)注入。在實(shí)現(xiàn)上接口注入最為復(fù)雜。不僅要為負(fù)責(zé)注入的方法建一個(gè)接口,還要為每種調(diào)用者配套一個(gè)注入器類,這些注入器類又實(shí)現(xiàn)一個(gè)公共的注入器接口,然后由一個(gè)容器創(chuàng)建注入器,注入器再調(diào)用它配套的那個(gè)對(duì)象實(shí)現(xiàn)的特定注入接口里的方法。是不是感覺要被繞暈了?那就直接忽略它,因?yàn)檫@樣折騰的結(jié)果是和其他形式相比沒有優(yōu)勢(shì),現(xiàn)實(shí)中也很少有人使用。
最后要比較的是構(gòu)造函數(shù)注入和設(shè)值注入。兩者的選擇實(shí)際上是一個(gè)更一般的問題:應(yīng)該通過構(gòu)造函數(shù)還是設(shè)值函數(shù)向?qū)ο髠鬟f信息?構(gòu)造函數(shù)是直觀的選擇,它畢竟就是被設(shè)計(jì)出來干這個(gè)的。對(duì)象的用戶最容易使用,也能夠最清晰地透過它的參數(shù)類型了解該對(duì)象需要的信息。構(gòu)造函數(shù)在對(duì)象初始化時(shí)必然會(huì)運(yùn)行,設(shè)值函數(shù)則要依靠用戶在恰當(dāng)?shù)臅r(shí)候調(diào)用。構(gòu)造函數(shù)還有一個(gè)特別的好處,即能夠用來設(shè)計(jì)不可變的(Immutable)對(duì)象。不可變的對(duì)象有很多好處,線程安全、易于測(cè)試等等。將構(gòu)造函數(shù)傳入的信息保存在只讀字段里,對(duì)象一旦創(chuàng)建,無論被調(diào)用了什么方法,狀態(tài)都和最初保持一樣。而使用設(shè)值函數(shù)時(shí),保存?zhèn)魅胄畔⒌淖侄尉筒荒苁侵蛔x的,使得以后可能通過再次調(diào)用設(shè)值函數(shù)或者其他方法修改該字段。盡管如此,構(gòu)造函數(shù)也有難以應(yīng)對(duì)需求的時(shí)候。遇上參數(shù)太多、類型相同不好區(qū)分多種版本的構(gòu)造函數(shù)等情況就要考慮使用設(shè)值函數(shù)。
總的來說,最常見和有用的形式是構(gòu)造函數(shù)注入和設(shè)值注入。因?yàn)樯厦嫠f的實(shí)現(xiàn)機(jī)制,容器返回的調(diào)用者對(duì)象內(nèi)部保有實(shí)現(xiàn)了被調(diào)用者接口的某個(gè)具體類型的對(duì)象。而在我們的例子里,播放器需要調(diào)用多種編解碼器的對(duì)象,所以只能采用方法調(diào)用注入的形式。與應(yīng)用服務(wù)定位器模式類似,可把負(fù)責(zé)注入的代碼放在播放器之外的一個(gè)控制器類中。


- 真空鍍膜
- 信號(hào)與系統(tǒng)習(xí)題指導(dǎo)
- 教你檢修液晶彩色電視機(jī)
- 電子產(chǎn)品組裝技能演練
- 通信專業(yè)綜合能力與實(shí)務(wù):終端與業(yè)務(wù)
- 等離子彩電維修代換技法揭秘
- Android語法范例參考大全
- 5G網(wǎng)絡(luò)規(guī)模部署與智慧運(yùn)營(yíng)
- 振動(dòng)信號(hào)的盲源分離技術(shù)及應(yīng)用
- 電力設(shè)備X射線檢測(cè)技術(shù)
- 信號(hào)與系統(tǒng)(第三版)
- SMT制造工藝實(shí)訓(xùn)教程
- 妙用微信微博就讀它
- 移動(dòng)通信原理、技術(shù)與系統(tǒng)
- Protel DXP 2004入門與提高