1. 首頁
  2. 輔助設計與工程計算

軟體技術方案設計原則

軟體技術方案設計原則

軟體開發是一項高強度的腦力勞動過程,到目前為止尚沒有辦法使軟體開發完全機械化,只能靠人腦去設計,也無法保證一個軟體完全沒有錯誤。同硬體產品相比,一方面是同功能的軟體產品不再需要一個生產過程,只需要複製就行了,而硬體產品卻需要重複生產;另一方面,軟體產品功能需求的變化遠比硬體產品多。這就造成軟體產品和硬體產品在成型之後面臨的問題是不同的,軟體產品主要是應付使用者功能需求的變化和擴充套件,硬體產品主要著眼於如何大規模地生產。硬體產品的大規模生產可以交給機器去執行,而軟體產品的需求變化還是要靠人腦去完成。以下是軟體技術方案設計原則,歡迎閱讀。

如何快速開發出符合使用者功能需求的軟體,如何保證開發出的軟體儘可能少地出現錯誤,如何使開發出的軟體能夠容易地適應使用者功能需求的變化和擴充套件,是所有軟體開發人員追求的目標;我想,也正是為了實現上述目標,才有了軟體設計原則和設計模式。

軟體開發是起始於面向過程的,為什麼會這樣呢?我想是因為面向過程地解決問題更直接,軟體本身就是一個解決問題的過程;面向過程的最大問題就是不容易把問題進行分解,再大的問題都要在一個過程裡面解決,從第一步直到最後一步;最多是把一個大過程分解成幾個順序執行的小過程,很考驗人的邏輯推理能力,開發出的軟體不容易維護、重用和擴充套件。面象物件的方法沒那麼直接,需要有一個抽象的過程,要把問題抽象成一個個物件,每個物件解決一個小問題,不同物件的組合就可解決不同的大問題;而且把物件跟日常的事物聯絡起來,產生了屬性、事件、方法這樣的概念,增加了物件的直觀性。

設計原則和設計模式都是針對面向物件的設計方法而提出來的,如果在軟體開發中還完全採用面向過程的方法,是無所謂設計原則和設計模式的。在軟體開發中,面向過程是起步,是基礎,沒什麼好研究的了;面向物件才是深入,是王道,需要不斷地去總結方法;下面的軟體設計都是指面向物件的設計方法。

根據前人總結的經驗,在軟體開發中,遵循一定的設計原則,靈活地採用一些設計模式,可以提高軟體的易維護性、可擴充套件性以及重用的機率。關於這方面最權威的著作恐怕就是Robert C. Martin寫的敏捷軟體開發一書了;關於這本書,個人閱讀的理解如下:

一、設計原則,該書提出瞭如下設計原則:

1、單一職責原則(SRP):一個類只實現一個功能;換一種說法,一個類只能有一個引起它變化的原因;在軟體工程中有一個要求,叫做高內聚;一個類只實現一個功能,無凝內聚度是最高的了;這一原則可以使一個類更好地被重用;當然,“一個功能”是相對的,在某種情況下,MODEM功能是一個單一功能,而在另一種情況下,可能就要把MODEM功能再分解成多個小功能;

2、開放封閉原則(OCP):開放是指一個類能夠擴充套件功能,封閉是指這個類對於功能修改是封閉的,也就是說不能修改其已有的程式碼和功能;要實現這一目標,關鍵是抽象;在客戶類中只使用抽象基類,在應用中子類繼承基類,並按實際需要擴充套件基類的功能;按更通俗的說法就是:介面不能改變,功能可以擴充套件;

3、子類替換原則(LSP):就是一個子類在任何情況下,都能替換掉它的基類;這是面向物件設計方法中實現繼承和多型必須遵循的一條基本原則,顯然也是開放封閉原則能夠實現的基礎;如何實現這一原則呢?那就是子類必須要有比基類相同或更弱的前置條件,相同或更強的後置條件;前置條件就是呼叫一個方法之前必須滿足的條件,後置條件就是一個方法執行之滿足的條件;為了更清楚地說明這一個問題,見下面的函式表示式:

Y = F(X);

F是一個函式,X是一個整型的輸入引數,Y是一個整型的返回值,如果F要求X>0,返回值Y>1,則X>0和Y>1就分別是F的前置條件和後置條件;如果F是基類A中的一個函式,B是A的一個子類,並擴充套件了F的功能,則B類中F的前置條件必須跟A類中的相同或更弱,也就B類中的F必須至少能接受X>0,如果能同時接收X<=0的條件更好,但不能要求X>1,這是一個X>0更強的條件;B類中的F必須保證返回值Y>1,當然如果能保證Y>10更好,但不能使返回值Y<=1,這樣就不滿足A類中F返回值Y>1的條件;不滿足子類替換原則最直接的後果就是使應用程式產生BUG;必須說明的是,在實際中是很難完全遵循子類替換原則的,必須作合理的假設,在這個假設的前提下遵循子類替換原則,這就是所謂契約設計;

4、依賴倒置原則(DIP):就是上層模組不能依賴於下層模組,兩者都應該依賴抽象;抽象不能依賴細節,細節應該依賴於抽象;對這一原則要靈活看待,因為這一原則和當前開發中常用元件開發方式看起來是相矛盾的;首先明確定義一下上層模組和下層模組,所謂上層模組是呼叫別人的模組,也可稱之為客戶模組,下層模組是被別人呼叫的模組,也可稱之為服務模組;顯然這是一個相對的概念,因為一個模組很可能同時即呼叫別的模組,又被另外的模組呼叫;依賴倒置原則告訴我們客戶模組和服務模組不能互相依賴,而只能依賴於一個抽象的基類;另外這個抽象基類的介面是由客戶模組決定,而不是由服務模組決定;也就是說客戶模組需要什麼,服務模組就提供什麼,而不是服務模組提供什麼,客戶模組就使用什麼;這頗有點當前企業信奉的一個原則:客戶就是上帝,客戶需要什麼,我們就提供什麼;而我們當前常用的元件程式設計中,每一個元件都是一個被別人呼叫的具體類,顯然是屬於細節和服務模組,我們呼叫元件,實際上就依賴了這些元件的模組;根據依賴倒置原則是不是就不能呼叫這些元件呢?當然不能這麼呆板,在設計中還有另一條原則,穩定依賴是沒有害處的;說到底,這些元件也是根據客戶需求制定出來的.,只不過是已經固化了的需求;而且這些元件經過了嚴密測試,是穩定的,依賴它們沒有害處;依賴倒置原則是針對我們自己的設計來說的;當然,如果我們自己設計的某一個服務類經過了嚴格的測試和大量的使用,都已經驗證沒有問題,也可以作為一個通用的元件,別人可以呼叫它,依賴它,沒有問題;

5、介面隔離原則(ISP):這一原則好象是單一職責原則的升級版,介面隔離原則強調的是當一個服務類需要被即有共同功能需求又有不同功能需求的客戶類使用時,不能在服務類中加進它的客戶不需要的方法,比如在服務類A的客戶中, B類客戶需要F方法,而C類客戶則不需要F方法,這時不能簡單地把F方法加到服務類A中以滿足B類客戶的需求,而應分離介面;比如另設計一個服務類D,其中包含F方法,並把共用的功能委託給A實現,這樣B客戶可以使用D,而C客戶繼續使用A;對這一原則我有所保留的是:如果F方法對C類沒有影響,直接加到A類中也無防,而且這種情況是很普遍;

6、共同封閉原則:這是針對包的;一個包對應用一個程式檔案,包含一到多個類,這些類具有共同的封閉性,要麼是都不能修改,要麼是隻能由同一原因引起修改;

7、共同重用原則:也是針對包的;不同類的通用性也是不一樣的,通用性最高的就是在所有專案中都可使用,比如我們用到的整合開發工具中的元件;有些類可能包含些行業特徵,只能這一行業類的軟體中使用,有些類包含了某一個專案的特徵,就可能在該專案中使用;但是一個包中的所有類的通用性都應該是一樣的,這樣才能保證包的重用度最大化;

二、設計模式,該書列出了以下常用的設計模式;

1、策略模式(STRATEGY):在一個擁有通用演算法的具體類中,把一些呼叫的方法委託給一個介面類實現,透過介面的不同實現,擴充套件不同的功能;策略模式能夠重用通用具體類,又易於擴充套件功能;

2、工廠模式(FACTORY):在一個工廠類中,傳入不同的引數,可生成不同的類(相同的介面,不同的實現);工廠模式易於擴充套件功能;

3、封裝模式(FAADE):對一個具有複雜介面的類(或API函式)進行封裝,並提供幾個簡單的介面供外部呼叫;封裝模式可以隔離複雜的介面,並使其使用變得簡單;

4、命令模式(COMMAND):上層模組要操作一組COMMAND物件,這些COMMAND物件都具有同樣的方法(不同的實現),上層模組在操作COMMAND物件時,只需要呼叫它們的方法,而不用關心方法的實現;在某些情況下,這種模式會極大地簡化系統;

5、組合模式(COMPOSITE):當A呼叫B,一對一的關係,需要改變為A呼叫多個B(或B的子類物件)時,不更改A的程式碼(比如在A中建立一個B或B的子類物件的列表,再依次從列表中取出物件呼叫),而是從B繼續一個子類C,在C類中建立B或B的子類物件的列表,重寫C類中相應的方法為依次呼叫列表中物件的方法,從而用A和C一對一的關係代替A和B之間一對多的關係,並保持A的程式碼不用更改;

6、觀察者模式(OBSERVER):在被觀察者中提供註冊介面(Register)用於註冊觀察者,所有註冊的觀察者都放入一個列表中,在觀察者中提供觀察介面(Update),用於接收被觀察者發出的通知;當被觀察者發生變化時,依次呼叫列表中的觀察者的觀察介面(Update),觀察者在觀察介面(Update)中,對感興趣的被觀察者變化進行處理;

7、代理模式(PROXY):主要用於代理資料庫操作,可以實現資料庫操作和業務操作的程式碼分離;實現模式如下:

應用程式呼叫一個介面A,B和C都實現A中的所有介面,其中B是知曉資料庫的代現,利用一個數據庫類D進行資料庫操作,然後委託相應的方法給C;代理模式使用不多,主要是在B類中把方法再委託給C,在大多數情況下都沒有必要;

8、介面卡模式(ADAPTER):在一個穩定的架構中,增加一個外部元件,但該元件的介面不符合架構的規範,這時就可建立一個介面卡類對外部元件進行封裝,介面卡的介面符合架構的規範(這樣才能納入架構),相應的方法委託給外部元件實現;這樣就可把外部元件納入到已有的架構中;另外,也可能是需要提供一個具有不同介面的元件給另外的客戶端使用,同時又要把該組合件的功能納入到已有的架構中,透過一個介面卡把元件納入架構,另外的客戶端直接使用元件;

最後說明:使用設計模式是有代價的,可能需要增加新的類,編寫額外的程式碼,增加複雜度;優勢就是,可以使系統更適應於功能需求的變化,包括功能修改和擴充套件,隔離變化等;可以提高程式碼的重用率;所以對於設計模式,不能生搬硬套,而應是順勢而為。