天天看點

簡話設計模式

簡話設計模式 作者:楊甯( 來自grapecity) 第一章 引言 1. 本文不适合…

本文不适合想通過本文來裝修房子的讀者;

本文不适合面向對象程式設計高手,會浪費你的時間。如果你願意抽出時間來閱讀本文,并提出寶貴的建議,非常感謝!什麼?你沒有聽說過設計模式?那你也敢稱高手?

2. 本文适合…

如果你對面向對象程式設計感興趣,而又沒有時間去讀Gang of Four的“Design Patterns Elements of Reusable Object-Oriented Software”(以下簡稱《設計模式》)。那麼,本篇文章将幫助你了解23種設計模式。

我第一次讀這本書是在每次晚睡之前,幾乎每次都先睡着。《設計模式》以一種嚴謹,系統化的風格來論述23種設計模式,原書可以說是面向對象程式設計的一個基礎教程,但是要領會其精髓,必須要花費一定的精力。本文的目的是為了幫助你更加友善地了解每一種設計模式,并不想成為原書的替代讀物。

本文無意于介紹面向對象的基本知識。是以,假設本文的讀者已經對面向對象的封裝性、繼承性和多态性有足夠的了解和認識。并能夠認識到可複用的面向對象設計的兩個原則:

● 針對接口程式設計,而不是針對實作程式設計;
● 優先使用對象組合,而不是類繼承。
3. 設計模式是什麼?

設計模式概念是由建築設計師Christopher Alexander提出:“每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的解決方案的核心。這樣,你就能一次又一次地使用該方案而不必做重複勞動。”上述的定義是對設計模式的廣義定義。我們将其應用到面向對象軟體的領域内,就形成了對設計模式的狹義定義。

我們可以簡單的認為:設計模式就是解決某個特定的面向對象軟體問題的特定方法。但嚴格的來說上述的認識是不準确的,難道面向對象軟體中隻有區區23個問題?當然不是。

為了能夠更加準确地了解這個概念,我們引入另外一個術語:架構(Framework)。架構這個詞彙在當今有了各種各樣的應用和含義。在設計模式中:架構(Framework)是構成一類特定軟體可複用設計的一組互相協作的類。

架構可以認為是一個适用于某個領域的軟體包。這個軟體包提供了相應領域的各個問題的解決方法。那麼,它和設計模式有什麼差別?

● 設計模式和架構針對的問題域不同:
設計模式針對面向對象的問題域;架構針對特定業務的問題域;

 

● 設計模式比架構更為抽象:
設計模式在碰到具體問題後,才能産生代碼;架構已經可以用代碼表示。
● 設計模式是比架構更小的體系結構元素:
架構中可以包括多個設計模式。

Tips:設計模式就像是在武功中基本的招式。我們将這些招式合理地組合起來,就形成套路(架構)。

4. 為什麼要用設計模式?

作為程式員都知道良好程式的一個基本标準:高聚合,低耦合。面向對象語言比結構化語言要複雜的多,不良或者沒有充分考慮的設計将會導緻軟體重新設計和開發。然而實際的設計過程中,設計人員更多的考慮如何解決業務問題,對于軟體内部結構考慮較少;設計模式則補充了這個缺陷,它主要考慮如何減少對象之間的依賴性,降低耦合程度,使得系統更易于擴充,提高了對象可複用性。是以,設計人員正确的使用設計模式就可以優化系統内部的結構。

第二章 概要簡介

在《設計模式》一書中,共包含23個模式。根據目的的不同,将它們分為三類:

● 建立型(Creational):解決如何建立對象的問題。
● 結構型(Structural):解決如何正确的組合類或對象的問題。
● 行為型(Behavioral):解決類或對象之間如何互動和如何配置設定職責的問題。

Tips:設計模式中經常會用到抽象(Abstract)和具體(Concrete)這兩個詞。抽象的含義是指它所描述的類(方法)是接口類(方法),具體的含義是指它所描述的類(方法)實作了相應的抽象類(方法)。

第三章 抽象工廠(Abstract factory) 1. 意圖

提供一個建立一系列相關或互相依賴對象的接口,而無需指定它們具體的類。

2. 分類

建立型模式。

3. 問題是什麼?

對于不熟悉這個模式的人都會對工廠(Factory)這個詞感到奇怪,為什麼會用這個詞?之後,我們還會碰到另一個模式——工廠方法(Factory method),是以先解釋一下工廠的意義:

房子是由牆,門,窗戶,地闆,天花闆,柱子組成的。如果我們為客戶編寫一個建造房子的軟體,我們會把牆,門,窗戶,地闆,天花闆,柱子看成不同的類:WallClass, DoorClass, WindowClass, CeilingClass, PillarClass。

現在我們建立一個新類A,這個類中有CreateWall(), CreateDoor(), CreateFloor(), CreateCeiling(), CreatePillar()五個方法,每個方法的功能就是建立并傳回相應的對象。如果把WallClass, DoorClass, WindowClass, CeilingClass, FloorClass, PillarClass的執行個體看成産品的話,那麼類A就像是一個生産這些産品的工廠。這就是使用工廠這個詞的原因。

Tips:A這個名字太糟,如果用HouseFactory會好一些。一般情況下,在你的系統使用某個模式,最好使用模式相應的關鍵字作為類或者方法的名字的一部分,這樣,你的同伴或者系統的代碼維護者就會明白你的用意。

我們的軟體完成了,客戶非常滿意。不過,我們的客戶想把這個軟體出口,他發現一個問題,這個軟體太本地化了,建造出來的都是中國式的房屋。是以他希望我們的軟體能夠建造出不同地域風格的房子。

這就是我們的問題!我們需要重新設計原系統,而且一次完成世界各地不同建築風格是不可能的。我們會先完成部分風格(客戶第一要投放軟體的國家的),然後再增加其他的…

4. 解決方法

1) 建立一個抽象工廠(Abstract Factory)類HouseFactory,在這個類中聲明:

CreateWall ()
CreateDoor ()
CreateFloor ()
CreateCeiling ()
CreatePillar ()

2) 建立相應的抽象産品(Abstract Product)類集:

Wall, Door, Floor, Ceiling, Pillar

3) 為不同風格建立相應的具體工廠(Concrete Factory)類(不要忘了實作關系),例如:

ChinaHouseFactory : HouseFactory
GreeceHouseFactory : HouseFactory

4) 為不同的風格建立相應的具體産品(Concrete Product)類(實作相應的抽象産品),例如:

ChinaWall : Wall
ChinaDoor : Door
GreeceWall : Wall
GreeceDoor : Door
5. 雜談

我想你一定明白如何靈活的建立和使用上面的一大堆類:

● 重複最後兩個步驟,就可以友善的增加新風格;
● 使用前面兩個步驟中聲明的抽象類來實作操作。

抽象工廠模式的重點不是聲明的那個抽象工廠類,而是它聲明的一系列抽象産品類,我們通過使用這些抽象産品類可以操作我們已經實作或者還未實作的具體産品類,并且保證了它們的一緻性。

你可能已經發現這個軟體不能建造你的兩層别墅,因為它沒有樓梯。為此,我們要定義Stair抽象類,還要增加CreateStair抽象方法;最重要的是我們要在已經完成的76種風格中增加相應的 Stair類,這可是個大麻煩。确實,抽象工廠模式在适應新的産品方面的能力是比較弱的。這是它的一個缺點。

 

第四章 生成器(Builder) 1. 意圖

将一個複雜對象的建構與它的表示分離,使得同樣的建構過程可以建立不同的表示。

2. 分類

建立型模式。

3. 問題是什麼?

在抽象工廠一章中,我們了解到了在全球建房系統中如何支援多種房屋的風格。新的問題是:使用者希望看到同樣結構下,不同風格房屋的外觀。比如:這個房子有一個地闆,一個天花闆,四面牆,一個門,一個窗戶。他希望看到中國風格房屋和希臘風格房屋的相應式樣。

4. 解決方法

1) 建立一個生成器(Buider)類:

Class HouseBuider
	{
		BuildHouse(){}
		BuildWall(int){}
		BuildDoor(int){}
		BuildWindow(int) {}
		BuildFloor(){}
		BuildCeiling() {}
		BuildPillar(){}
		GetHouse() { return Null;}
	}
      

在這個類中,我們對每一種房屋組成元素都定義了一個Build方法。并且定義了一個傳回構造結果的方法GetHouse。

2) 對每一種風格都定義一個具體的生成器(Concrete Builder)類:

ChinaHouseBuilder: HouseBuilder
GreeceHouseBuilder: HouseBuilder
…
      

并且在這些類中重載父類的方法。

3) 還有,各種類型的房屋類:

ChinaHouse
GreeceHouse
…
      
5. 如何使用

我們可以用下面的方法使用上面的類來解決我們的問題。

Class HosueSystem
{
object Create( builder HouseBuilder)
{
	builder.BuildHouse();
	builder.BuildFloor();
	builder.BuildCeiling();
	builder.BuildWall(1);
	builder.BuildWall(2);
	builder.BuildWall(3);
	builder.BuildWall(4);
	builder.Door(1);		// 在Wall1上建一個門
	builder.Window(2); 	// 在Wall2上建一個窗戶

	return builder.GetHouse();
}
}
      

隻要向通過HouseSystem.Create方法傳入不同的生成器就可以得到不同風格的結果。

事實上,HouseSystem也是生成器模式的一個參與者,叫做導向者(Director)。注意的問題:

● 生成器(HouseBuilder)類中要包含每一種構件元素的生成方法。比如:樓梯這個元素在某些建築風格中沒有,在其他風格中有,也要加上BuilderStair方法在HouseBuilder中。
● 生成器(HouseBuilder)類不是抽象類,它的每一個生成方法一般情況下什麼都不做。這樣,具體生成器就不必考慮和它無關的元素的生成方法。
6. 雜談

1) 也許你有這樣的想法:生成器模式似乎過于複雜,可以直接在具體House類中定義CreateHouse方法就可以了,例如:

Class ChinaHouse
{
	ChinaHouse CreateHouse()
{	ChinaHouse house;
house = new ChinaHouse();
house.Add(new Floor());
house.Add(new Ceiling());
house.Add(new Wall(1));
house.Add(new Wall(2));
house.Add(new Wall(3));
house.Add(new Wall(4));
house.Add(new Door(1));		// 在Wall1上建一個門
house.Add(new Window(2)); 	// 在Wall2上建一個窗戶

return house;
}
}
      

而生成器模式至少用了兩個類來解決這個問題:導向者類和具體生成器類。

那麼,生成器模式好在哪裡?答案就是職責配置設定。生成器模式将一個複雜對象的生成這一職責作了一個很好的配置設定。它把構造過程放到導向者的方法中,把裝配過程放到具體生成器類中。我們看看下面的說明。

2) HouseSystem類(導向者)可以非常精細的來構造House。而這個生成過程,對于産品類(ChinaHouse, GreeceHouse …)和生成器類 (ChinaHouseBuilder, GreeceHouseBuilder)都沒有必要關心。具體生成器類則考慮裝配元素的問題。

7. 活用

生成器模式可以應用在以下的問題上: 

● 将系統的文檔格式轉換到其他的格式上(每一種格式的文檔都相當于一個産品)。 

● 編譯問題(文法分析器是導向者,編譯結果是産品)。 

關于作者:

楊甯是GrapeCity公司海外應用開發部技術骨幹。從事多年的程式開發,有Unix,Windows平台上的開發經驗。對VB,C#, VB.Net,XML有比較豐富的認識。喜愛研究OO的程式設計,分析,設計,項目管理等相關技術。喜歡學習新的技術。

繼續閱讀