通過前面兩篇,我想各位朋友對“面向接口編程”的思想有了一定認(rèn)識,并通過第二篇的例子,獲得了一定的直觀印象。但是,第二篇中的例子旨在展示面向接口編程的實現(xiàn)方法,比較簡單,不能體現(xiàn)出面向接口編程的優(yōu)勢和這種思想的內(nèi)涵。那么,這一篇作為本系列的終結(jié)篇,將通過分析幾個比較有深度的模式或架構(gòu),解析隱藏其背后的面向接口思想。這篇我將要分析的分別是MVC模式和.NET平臺的分層架構(gòu)。 這篇的內(nèi)容可能會比較抽象,望諒解。 1.從MVC開始MVC簡介: 本文不打算詳細(xì)解釋MVC架構(gòu),而是把重點放在其中的面向接口思想上。所以在這里,只對MVC做一個簡略的介紹。 MVC是一種用于表示層設(shè)計的復(fù)合設(shè)計模式。M、V、C分別表示模型(Model)、View(視圖)、Controller(控制器)。它們的職責(zé)如下: 模型:用于存儲應(yīng)用中的數(shù)據(jù)及運行邏輯,是應(yīng)用的實體。 視圖:負(fù)責(zé)可視部分,用于與用戶交互及呈現(xiàn)數(shù)據(jù)。視圖只負(fù)責(zé)顯示,不負(fù)責(zé)將用戶的操作行為解釋給模型。 控制器:負(fù)責(zé)將用戶的行為解釋給模型。根據(jù)指定的策略和用戶的操作,調(diào)用模型的邏輯。 關(guān)于三者的關(guān)系,我畫了一張圖,大家請看:

它們之間的交互有以下幾種: 1.當(dāng)用戶在視圖上做任何需要調(diào)用模型的操作時,它的請求將被控制器截獲。 2.控制器按照自身指定的策略,將用戶行為翻譯成模型操作,調(diào)用模型相應(yīng)邏輯實現(xiàn)。 3.控制器可能會在接到視圖操作時,指定視圖做某些改變。 4.當(dāng)模型的狀態(tài)發(fā)生改變時,將通過某種方式通知視圖。 5.視圖可以從模型獲取狀態(tài),從而改變自己的顯示。
MVC介紹完了,那么可能會有人問,我們的主題呢?面向接口思想呢?其實,MVC中處處都存在面向接口的影子。下面,我對其中幾個側(cè)面進(jìn)行解釋。 1.首先我們可以看到,視圖和模型是有直接交互的,也就是上面的4、5兩點。但是有一點可能會讓你吃驚:它們兩個誰也不“認(rèn)識”誰,即它們相互并不知道對方是做什么的、有什么屬性、有什么方法,但是它們能交互。這是怎么做到的呢?因為它們個各知道對方實現(xiàn)了某一個接口。 此乃面向接口思想一大作用:使相互不認(rèn)識的類進(jìn)行交互。這樣做是很有好處的,首先它們之間的耦合度大大降低,其次雙方都可以進(jìn)行替換,只要實現(xiàn)了相同的接口,就沒有問題。 打個不太恰當(dāng)?shù)谋扔鳌N覀兌贾?20這個電話號碼,是急救電話。其實120就是個接口,因為當(dāng)你撥打這個電話時,你不知道那邊是哪所醫(yī)院,甚至不知道那邊是不是醫(yī)院,你只知道電話那頭的地方可以救人,也可以說實現(xiàn)了IHelp接口。這樣,你通過一個號碼可以說同全部的救人機構(gòu)聯(lián)系起來了,當(dāng)有緊急事件,接線控制那邊會將你的請求接到最近可用的機構(gòu),你就可以最快的得到幫助。 現(xiàn)在我們假設(shè)沒有使用面向接口思想,來看看會發(fā)生什么恐怖的事情:首先,我家的120號碼是綁定在本市第一人民醫(yī)院的,即當(dāng)我撥打120時,只能撥通第一人民醫(yī)院。如果有一天我食物中毒了,急忙撥通了120,但是電話那邊告訴我他們醫(yī)院的救護車都派出去了,我問那怎么接通別家醫(yī)院的電話,那邊的MM很溫柔的告訴我,讓我打電話給網(wǎng)通公司,然后重新為我布線。于是我吐血而亡…… 言歸正傳。這里,我要引入一個設(shè)計模式,叫觀察著(Observer)模式。這個模式大約是這樣的:整個模式中有兩種實體:觀察者和被觀察者,它們分別實現(xiàn)一個接口,這里我們姑且叫做IObserver與IObserverSubject。IObserver只有一個方法,例如叫Update,當(dāng)被觀察者狀態(tài)改變時,調(diào)用這個方法,用來通知觀察者。IObserverSubject接口有兩個方法,都是供觀察者調(diào)用。一個用來將觀察者注冊為此被觀察者的觀察對象,另一個用于將觀察者移除。 一般情況下,一個被觀察者對應(yīng)多個觀察者。 在MVC中,視圖是觀察者,模型是被觀察者,當(dāng)模型狀態(tài)改變時,調(diào)用所有觀察者的Update方法,通知視圖模型有變,視圖在Update方法里寫下響應(yīng)代碼,完成操作。通過這個方法,視圖和模型就可以在僅依賴接口的情形下進(jìn)行交互,而不必強耦合,而且在模型不變的情況下,視圖可以隨意替換。(只要實現(xiàn)了IObserver)
2. 在MVC中另一個使用接口的地方就是控制器,這里我要首先引入一個設(shè)計模式:策略模式(Strategy)。在MVC中,控制器就使用了這個模式。 剛才我說過,視圖負(fù)責(zé)與用戶交互,但是,它只負(fù)責(zé)界面顯示部分,至于當(dāng)用戶做了某個操作(如單擊某個按鈕)后系統(tǒng)應(yīng)該怎么反應(yīng),視圖并不負(fù)責(zé),它只是將這個動作交給控制器,控制器根據(jù)內(nèi)置的策略,將用戶操作翻譯成模型的邏輯。這就是說,同一個視圖、同一種操作,模型可以做出不同的反應(yīng),這取決與控制器的內(nèi)置策略。所以,我們的系統(tǒng)中可以有很多控制器,它們有不同的策略,當(dāng)視圖希望改變策略時,它可以更換控制器。怎么實現(xiàn)呢?這就需要視圖不能和具體控制器耦合,而是要僅依賴一個控制器接口(如IController),并聚合一個IController的實例。當(dāng)希望更改策略時,可以在系統(tǒng)運行時動態(tài)更換Controller,這就是策略模式的實現(xiàn)。
關(guān)于MVC的接口思想就先介紹到這里。其實MVC中還有很多地方用到面向接口,由于本文不是專門介紹MVC或設(shè)計模式的,所以對用到的模式?jīng)]有做詳解,而是把重點放在其中的面向接口思想上。如果沒有設(shè)計模式的基礎(chǔ),讀上文可能會有些困難,希望各位見諒!我打算在以后專門寫文章來解析MVC。
2..NET平臺下分層架構(gòu)的面向接口思想 我們知道,在做大一點的系統(tǒng)應(yīng)用時(特別是B/S架構(gòu)),比較好的方法是分層架構(gòu)。所謂分層架構(gòu),是指將系統(tǒng)從職責(zé)上分成若干層,每層各司其職,上層依賴下層完成操作。 在.NET平臺上,比較經(jīng)典的分層架構(gòu)是三層架構(gòu),從下到上依次是:數(shù)據(jù)訪問層、業(yè)務(wù)邏輯層、表示層。各層職責(zé)如下: 數(shù)據(jù)訪問層:負(fù)責(zé)與數(shù)據(jù)源交互,完成數(shù)據(jù)訪問等一系列操作。 業(yè)務(wù)邏輯層:完成與系統(tǒng)業(yè)務(wù)有關(guān)的邏輯操作。 表示層:負(fù)責(zé)與用戶交互、呈現(xiàn)數(shù)據(jù)等一切與系統(tǒng)表示有關(guān)的操作。
剛才我們說過,分層架構(gòu)下是向下依賴的(不考慮依賴倒置),也就是業(yè)務(wù)邏輯層要調(diào)用數(shù)據(jù)訪問層完成與數(shù)據(jù)源有關(guān)的操作,而表示層調(diào)用業(yè)務(wù)邏輯層完成業(yè)務(wù)邏輯工作。但是,表示層對數(shù)據(jù)訪問層是沒有依賴的。 在這個架構(gòu)中,每一層都不是一個類,而是一個類族,例如,在一個CMS系統(tǒng)中,數(shù)據(jù)訪問層可能會有一系列的類,分別負(fù)責(zé)用戶、文章、評論等業(yè)務(wù)實體的數(shù)據(jù)訪問操作,而業(yè)務(wù)邏輯層也一樣。如果我們直接依賴,即業(yè)務(wù)邏輯層實例化數(shù)據(jù)訪問層的類,表示層再實例化業(yè)務(wù)邏輯層的類,會造成強耦合。如果我想把數(shù)據(jù)庫從SQLServer換成MySQL,則要改變整個業(yè)務(wù)邏輯層代碼,這是個不好的設(shè)計。(還記得“開放-關(guān)閉”原則嗎)所以,一般的做法是,為數(shù)據(jù)訪問層和業(yè)務(wù)邏輯層分別定義一族接口,業(yè)務(wù)邏輯層不依賴具體的數(shù)據(jù)訪問層,而是僅依賴數(shù)據(jù)訪問層的接口族,表示層也一樣,依賴業(yè)務(wù)邏輯層的接口族。如此一來,當(dāng)要更換數(shù)據(jù)庫時,我們就不必改寫整個業(yè)務(wù)邏輯層,因為業(yè)務(wù)邏輯層里根本沒有任何數(shù)據(jù)訪問層中的具體類,而全是通過接口實現(xiàn)的。在.NET中,只要配合配置文件和反射機制,再運用Abstract Factory設(shè)計模式,就可以實現(xiàn)“依賴注入”,即在不改動代碼的情況下根據(jù)配置選擇相應(yīng)的層次組件。這樣,我們就可以為不通數(shù)據(jù)庫分別實現(xiàn)數(shù)據(jù)訪問層,也可以編寫ORM的數(shù)據(jù)訪問層,甚至是基于XML的,只要實現(xiàn)了數(shù)據(jù)訪問層接口族,就可以和業(yè)務(wù)邏輯層無縫連接,從而極大提高了軟件的靈活性和可維護性。當(dāng)然要更改業(yè)務(wù)邏輯層也是一樣。 如果說,前面的例子都是從微觀視角討論接口,那么,這個例子則從宏觀視角展現(xiàn)了面向接口編程的內(nèi)涵和優(yōu)勢。很抱歉在這里不能對這個架構(gòu)深入講解,有興趣的朋友可以參考微軟的官方示例.NET PetShop4。(但是請注意,這個示例中業(yè)務(wù)邏輯層沒有定義接口族,而是強耦合于表示層中,這可能是因為考慮到在這個系統(tǒng)中業(yè)務(wù)邏輯沒有更改的可能。另外由于是個示例,不是真正的B2C系統(tǒng),所以業(yè)務(wù)邏輯層很簡單。)
好了,本系列文章就到這里。希望各位朋友通過這三篇文章,能對“面向接口編程”有一定的了解。當(dāng)然,我只是起到一個拋磚引玉的作用,其真正的內(nèi)涵和精髓,還需要各位從實踐中慢慢認(rèn)識。還有,就是面向接口思想不是孤立的,它和設(shè)計模式等內(nèi)容都是面向?qū)ο蟠笙抵械木A,而且是相互滲透、相互聯(lián)系的。其實,很多設(shè)計模式就是面向接口思想的體現(xiàn)。我們應(yīng)該把這些放在一起學(xué)習(xí),從而真正提供自己的面向?qū)ο笏伎寄芰蛯崙?zhàn)能力。
|