根據Linda Rising的《Pattern Almanac》一書,已知的架構模式有七十多種。這是一個只多不少的統計,其中包括了很多通常認為是設計模式的模式,比如Bridge,Facade,Interpreter,Mediator等模式通常認為是設計模式,但是在許多情況下,也可以作為架構模式出現,因此也常常被當作架構模式。
Layers架構模式
在收集到使用者對軟體的要求之後,架構設計就開始了。架構設計一個主要的目的,就是把系統劃分成為很多"板塊"。劃分的方式通常有兩種,一種是橫向的劃分,一種是縱向劃分。
橫向劃分將系統按照商業目的劃分。比如一個書店的管理系統可以劃分成為進貨、銷售、庫存管理、員工管理等等。
縱向劃分則不同,它按照抽象層次的高低,將系統劃分成"層",或叫Layer。比如一個公司的內網管理系統通常可以劃分成為下面的幾個Layer:
一、網頁,也就是使用者介面,負責顯示資料、接受使用者輸入;
二、領域層,包括JavaBean或者COM對象、B2B服務等,封裝了必要的商業邏輯,負責根據商業邏輯決定顯示什麼資料、以及如何根據使用者輸入的資料進行計算;
三、資料庫,負責儲存資料,按照查詢要求提供所儲存的資料。
四、作業系統層,比如Windows NT或者Solaris等
五、硬體層,比如SUN E450伺服器等
有人把這種Layer叫做Tier,但是Tier多帶有物理含義,不同的Tier往往位於不同的電腦上,由網路連接起來,而Layer是純粹邏輯的概念,與物理劃分無關。
Layers架構模式的好處是:
第一、任何一層的變化都可以很好地局限於這一層,而不會影響到其他各層。
第二、更容易容納新的技術和變化。Layers架構模式容許任何一層變更所使用的技術
Fa?ade架構模式
外部與一個子系統的通訊必須通過一個統一的門面(Facade)對象進行,這就是Facade模式。
現代的軟體系統都是比較複雜的,設計模式的任務就是協助設計師處理複雜系統的設計。
設計師處理複雜系統的一個常見方法便是將其"分而治之",把一個系統劃分為幾個較小的子系統。但是這樣做了以後,設計師往往仍然會發現一個子系統內仍然有太多的類型要處理。而使用一個子系統的使用端往往只關注一些特定的功能,卻要同時與子系統內部的許多個物件打交道後才能達到目的,請見下面的對象圖。
這就是一種不便,它使得系統的邏輯變得不必要的複雜,維護成本提高,複用率降低。
用一個範例說明,中國大陸的醫院便是一個子系統,按照部門職能,這個系統可以劃分為挂號、門診、劃價、化驗、收銀、取藥等。看病的病人要與這些部門打交道,就如同一個子系統的使用端與一個子系統的各個類型打交道一樣,不是一件容易的事情。
首先病人必須先挂號,然後門診。如果醫生要求化驗,病人必須首先劃價,然後繳款,才能到化驗部門做化驗。化驗後,再回到門診室,請見下面的對象圖。
圖5、描述病人在醫院裡的體驗。圖中的方框代表醫院。 |
解決這種不便的方法便是引進Facade模式。仍然通過醫院的範例說明,可以設定一個接待員的位置,由接待員負責代為挂號、劃價、繳費、取藥等。這個接待員就是Facade模式的體現,病人只接觸接待員,由接待員負責與醫院的各個部門打交道,請見下面的對象圖。
圖6、描述經過Facade模式的改裝後,病人在醫院裡的體驗。圖中的方框代表醫院。 |
Facade模式要求一個子系統的外部與其內部的通訊必須通過一個統一的門面(Facade)對象進行。Facade模式提供一個高等級的介面,使得子系統更便於使用。
使用了Facade模式之後,本章的第一個圖中所描述的一個子系統的使用端對象所面對的複雜關係就可以簡化為下面這個樣子。
描述經過Facade模式的改裝後,一個子系統的使用端與子系統的關係。圖中的大方框代表一個子系統。
就如同醫院的接待員一樣,Facade模式的門面類型將使用端與子系統的內部複雜性分隔開,使得使用端只需要與門面對象打交道,而不需要與子系統內部的很多個物件打交道。
Mediator架構模式
Mediator模式封裝了一系列對象相互作用的方式,使得這些對象不必互相明顯參照;從而使它們可以較鬆散地耦合。當這些對象中的某些對象之間的相互作用發生改變時,不會立即影響到其它的一些對象之間的相互作用;從而可以保證這些相互作用可以彼此獨立地變化。
在下面的中有大量的對象,這些對象既會影響別的對象,又會被別的對象所影響,因此常常叫做同事(Colleague)對象。這些同事對象通過彼此的相互作用形成系統的行為。可以看出,幾乎每一個對象都需要與其它的對象發生相互作用,而這種相互作用表現為一個對象與另一個對象的直接耦合。
通過引入調停者對象(Mediator),可以將系統的網狀結構變成以中介者為中心的星形結構,如所示。在這個星形結構中,同事對象不再通過直接的聯絡與另一個對象發生相互作用;相反地,它通過調停者對象與另一個對象發生相互作用。調停者對象的存在保證了對象結構上的穩定,也就是說,系統的結構不會因為新對象的引入造成大量的修改工作。
圖9、這是一個使用了Mediator架構模式之後的結構圖 |
比較傳統的設計方法,物件導向的技術可以更好地協助設計師管理更為複雜的系統。一個好的物件導向的設計可以使對象之間增加協作性(Collaboration),減少耦合度(Coupling)。一個深思熟慮的設計會把一個系統分解為一群相互協作的同事對象,然後給每一個同事對象以獨特的責任,恰當的配置它們之間的協作關係,使它們可以在一起工作。
在Mediator模式中,所有的成員對象都可以協調工作,但是又不直接相互管理。這些對象都與一個處於中心地位的調停者對象發生緊密的關係,由這個調停者對象進行協調工作。這個協調者對象叫做調停者(Mediator),而調停者所協調的成員對象稱做同事(Colleague)對象。
在Colleague對象內部發生的事件會影響到所有的同事,但是這種影響不是以直接管理的方式直接傳到其它的對象上的。記住在小組的成員增加時,這樣的相互作用關係是以比指數更快的方式增加的。相反,這種影響僅僅直接影響到調停者對象,而調停者對象反過來會協調其它的同事,形成整個系統的行為。
如果小組的成員增加時,調停者對象可能會面臨修改,而其它的同事則可以裝做不知道這個新的成員一樣,不必修改。反過來,如果小組的成員之一被從系統中刪除的話,調停者對象需要對此做出修改,而小組中其它的同事則不必改動。
Interpreter架構模式
給定一個語言之後,Interpreter模式可以定義出其文法的一種表示,並同時提供一個直譯器;使用端可以使用這個直譯器來解釋這個語言中的句子。
如果某一類型問題一再地發生的話,那麼一個有意義的做法就是將此類型問題的各個執行個體表達為一個簡單語言中的語句。這樣就可以建造一個直譯器,通過解釋這些語句達到解決問題的目的。
例如,依照一個匹配模式搜尋字串便是一個常見的問題。與其為每一個匹配模式建造一個特定的演算法,不如建造一個一般性的演算法處理各種常規運算式。當接到一個指定的常規運算式時,系統使用一個直譯器解釋這個常規運算式,從而對字串進行匹配。
再比如VBA(Visual Basic for Applications)就不僅僅出現在微軟的Office系列軟體中,並且可以供第三廠家出產的軟體嵌入使用;Crystal Reports報表產生軟體也包括了一個便於使用的宏語言,使使用者可以執行較為複雜的命令操作。一般而言,將VBA或者其它的語言軟體嵌入到自己的軟體產品中,可以使產品定製化(Customization)能力大大增強,但是這些宏語言引擎往往都很昂貴。
現在要介紹的Interpreter模式將描述怎樣在有了一個簡單的文法後,使用模式設計解釋這些語句。熟悉了這個模式以後,一個沒有接收過形式語言和編譯器的正規訓練的設計師也可以自行設計一個簡單的直譯器,以便為使用端提供一個簡單語言,或者在系統內部使用一個簡單語言描述一個合適的問題。
語言、直譯器和剖析器
Interpreter模式只描述直譯器是怎樣工作的,並不指明怎樣在執行時建立新的直譯器。雖然廣義地講直譯器不一定要有一個剖析器(Parser),但是使用剖析器仍然是最常見的建立直譯器的辦法。一個剖析器可以從一個檔或命令列讀入文字性命令,並建立直譯器。
剖析器是一種能夠識別文字並將文字按照一定規則進行分解以便進一步處理的對象。剖析器能夠識別的字串叫做語言。通常建立的小型電腦語言是與環境無關的語言,也就是遵循一定的文法的文字模式,所謂文法,便是決定怎樣將語言的元素組合起來的規則的集合。剖析器便是根據組合規則將字串分解的。
抽象地講,語言並不一定是以字串的形式表達的。在Interpreter模式裡面所提到的語言是指任何直譯器對象能夠解釋的任何組合。在Interpreter模式中,需要定義一個代表文法的命令類型的等級結構,也就是一系列的組合規則;每一個命令對象都有一個解釋方法,代表對命令對象的解釋。
命令對象的等級結構中的對象的任何排列組合都是一個語言,而剖析器的工作便是將一個文字性語言翻譯成為等效的直譯器語言。因此,直譯器往往需要剖析器。
認識Jack嗎?
剖析器產生器(Parser Generator),常常稱為編譯器的編譯器(Compiler Complier)。Sun Microsystem提供一個專為Java程式員發明的強大的剖析器產生器,最初叫做Jack,後來改名為JavaCC。
要使用JavaCC,必須使用它提供的指令碼語言編寫一個指令碼,然後執行JavaCC產生Java原始碼。這產生的原始碼就是所需的剖析器。現在Sun已經不再負責JavaCC的研發,對JavaCC感興趣的讀者可以從http://www.experimentalstuff.com/Technologies/JavaCC得到免費的JavaCC和相關資料。
JavaCC最早命名為Jack是為了與一個早就廣泛使用的剖析器產生器YACC諧音。如果讀者已經熟悉了YACC,可以使用YACC達到同樣的目的;只是相比之下JavaCC更容易得到Java程式員的喜愛。