.NET 進階架構師0005 架構師之路(4)---物件導向的設計原則

來源:互聯網
上載者:User

標籤:功能   需要   .net   appear   代碼   就會   電子郵件   改進   tco   

1         OO的設計原則

採用物件導向的分析和設計思想,為我們分析和解決問題提供了一種全新的思維方式。我們在拿到需求之後(略去OOA,以後補全),接下來的問題就是:如何對系統進行物件導向的設計呢?

按照軟體工程的理論,物件導向的設計要解決的核心問題就是可維護性和可複用性。尤其是可維護性,它是影響軟體生命週期重要因素,通常情況下,軟體的維護成本遠遠大於初期開發成本。

一個可維護性很差的軟體設計,人們通常稱之為“臭味”的,形成的原因主要有這麼幾個:過於僵硬、過於脆弱、複用率低或者黏度過高。相反,一個好的系統設計應該是靈活的、可擴充的、可複用的、可插拔的。在20世紀80到90年代,很多業內專家不斷探索麵向對象的軟體設計方法,陸續提出了一些設計原則。這些設計原則能夠顯著地提高系統的可維護性和可複用性,成為了我們進行物件導向設計的指導原則:

1、 單一職責原則SRP

每一個類應該只專註於做一件事。

2、 “開-閉”原則OCP

每一個類應該是對擴充開放,對修改關閉。

3、 裡氏代換原則LSP

避免造成衍生類別的方法非法或退化,一個基類的使用者應當不需要知道這個衍生類別。

4、 依賴倒轉原則DIP

用依賴於介面和抽象類別來替代依賴容易變化的具體類。

5、 介面隔離原則ISP

應當為客戶提供儘可能小的介面,而不是提供大的介面。

 

其中,“開-閉”原則是物件導向的可複用設計的基石,其他設計原則是實現“開-閉”原則的手段和工具。

我會為大家一一進行講解。

2         單一職責原則SRP(Single-Responsibility Principle)2.1      什麼是單一職責

單一職責就是指一個類應該專註於做一件事。現實生活中也存在諸如此類的問題:“一個人可能身兼數職,甚至於這些職責彼此關係不大,那麼他可能無法做好所有職責內的事情,所以,還是專人專管比較好。”我們在設計類的時候,就應該遵循這個原則:單一職責。

我們以計算機編程為例:

在有些人眼裡,計算機就是一件東西,是一個整體,所以它把這個需求進行了抽象,最終設計為一個Calculator類,代碼如下:

 

class Calculator{

public String calculate() {

 Console.Write("Please input the first number:");

       String strNum1 = Console.ReadLine();

Console.Write(Please input the operator:");

String strOpr= Console.ReadLine();

Console.Write("Please input the second number:");

       String strNum2 = Console.ReadLine();

        String strResult = "";

       if (strOpr == "+"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) + Convert.ToDouble(strNum2));

}

else if (strOpr == "-"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) - Convert.ToDouble(strNum2));

}

else if (strOpr == "*"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) * Convert.ToDouble(strNum2));

}

else if (strOpr == "/"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) / Convert.ToDouble(strNum2));

}

         Console.WriteLine("The result is " + strResult);

    }    

}

另外,還有一部分人認為:計算機是一個外殼和一個處理器的組合。

class Appearance{

public int displayInput(String &strNum1,String &strOpr, String &strNum2) {

Console.Write("Please input the first number:");

       strNum1 = Console.ReadLine();

Console.Write(Please input the operator:");

strOpr= Console.ReadLine();

Console.Write("Please input the second number:");

       strNum2 = Console.ReadLine();

 

return 0;

}

public String displayOutput(String strResult) {

Console.WriteLine("The result is " + strResult);

    }

}

class Processor{

public String calculate(String strNum1,String strOpr, String strNum2){

       String strResult = "";

       if (strOpr == "+"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) + Convert.ToDouble(strNum2));

}

else if (strOpr == "-"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) - Convert.ToDouble(strNum2));

}

else if (strOpr == "*"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) * Convert.ToDouble(strNum2));

}

else if (strOpr == "/"){

          strResult = Convert.ToString(Convert.ToDouble(strNum1) / Convert.ToDouble(strNum2));

}

return strResult;

}

}

 

 

    為什麼這麼做呢?因為外殼和處理器是兩個職責,都是很容易發生需求變動的因素,所以把他們放到一個類中,違背了單一職責原則。

 

比如,使用者可能對計算機提出以下要求:

第一,目前已經實現了“加法”、“減法”、“乘法”和“除法”,以後還可能出現“乘方”、“開方”等很多運算。

第二,現在人機介面太簡單了,還可能做個Windows計算機風格的介面或者Mac計算機風格的介面。

所以,把一個類Calculator 拆分為兩個類Appearance和Processor,更容易應對需求變化。如果介面需要修改,那麼就去修改Appearance類;如果處理器需要修改,那麼就去修改Processor類。

我們再舉一個郵件的例子。我們平常收到的郵件內容,看起來是一封信,實際上內部有兩部分組成:郵件標頭和郵件體。電子郵件的編碼要求符合RFC822標準。

第一種設計方式是這樣:

interface IEmail {

    public void setSender(String sender);

    public void setReceiver(String receiver);

    public void setContent(String content);

}

 

class Email implements IEmail {

    public void setSender(String sender) {// set sender; } 

    public void setReceiver(String receiver) {// set receiver; }   

    public void setContent(String content) {// set content; }

}

 

這個設計是有問題的,因為郵件標頭和郵件體都有變化的可能性。

1、郵件標頭的每一個域的編碼,可能是BASE64,也可能是QP,而且域的數量也不固定。

2、郵件體中封裝的郵件內容可能是PlainText類型,也可能是HTML類型,甚至於流媒體。

所謂第一種設計方式違背了單一職責原則,裡面封裝了兩種可能引起變化的原因。

我們依照單一職責原則,對其進行改進後,變為第二種設計方式:

interface IEmail {

 public void setSender(String sender);

 public void setReceiver(String receiver);

 public void setContent(IContent content);

}

 

interface IContent {

 public String getAsString();

}

 

class Email implements IEmail {

 public void setSender(String sender) {// set sender; }   

 public void setReceiver(String receiver) {// set receiver; } 

 public void setContent(IContent content) {// set content; }

}

有的資料把單一職責解釋為:“僅有一個引起它變化的原因”。這個解釋跟“專註於做一件事”是等價的。如果一個類同時做兩件事情,那麼這兩件事情都有可能引起它的變化。同樣的道理,如果僅有一個引起它變化的原因,那麼這個類也就只能做一件事情。

2.2      單一職責原則的使用

單一職責原則的尺度如何掌握呢?我怎麼能知道該拆分還是不應該拆分呢?原則很簡單:需求決定。如果你所需要的計算機,永遠都沒有外觀和處理器變動的可能性,那麼就應該把它抽象為一個整體的計算機;如果你所需要的計算機,外殼和處理器都有可能發生變動,那麼就必須把它拆離為外殼和處理器。只能有一個原因可能引起計算機的變化。

單一職責原則把相同的職責進行彙總,避免把相同的職責分散到不同的類之中,這樣就可以控制變化,把變化限制在一個地方,防止因為一個地方的變動,引起更多地方的變動的“漣漪效應”。單一職責原則實際上消除了對象之間的耦合,避免一個類承擔過多的職責。單一職責不是說一個類就只有一個方法,而是單一功能。

我們在使用單一職責原則的時候,牢記以下幾點:

A、一個設計合理的類,應該僅有一個可以引起它變化的原因,即單一職責,如果有多個原因可以引起它的變化,就必須進行分離;

B、在沒有需求變化徵兆的情況下,應用SRP或其他原則是不明智的,因為這樣會使系統變得很複雜,系統由一堆細小的顆粒組成,這純屬於沒事找抽;

C、在需求能夠預計或實際發生變化時,就應該使用SRP原則來重構代碼,有經驗的設計師、架構師對可能出現的需求變化很敏感,設計上就會具有前瞻性。

 

.NET 進階架構師0005 架構師之路(4)---物件導向的設計原則

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.