設計模式學習總結:(1)面向對象五大基本原則

 

最近學習了李建忠老師設計模式教程,感受有一種豁然開朗的感受。數據庫

"每個模式描述了在咱們周圍不斷重複發生變化的問題,以及該問題的解決方案的核心。這樣,你就能一次又一次的使用該方案而沒必要重複的勞動"——christopher alexander編程

固然,設計模式的有本很著名的書,常考教程:設計模式

書名:設計模式:可複用面向對象軟件的基礎學習

 

固然,我不是推銷書的。主要是書名很明確的給出了,軟件設計模式中使用的手法,經常使用就是面向對象,確實,在我第一次聽到面向對象的時候,個人理解是徹底不懂,在個人二次學習面向對象的時候,個人理解是封裝,繼承,多態。而到如今,我以爲,咱們的最終目的,仍是編寫可複用,可擴展,便於維護的軟件。固然,如何把面向對象的特性運用到極致,我想還得深刻去理解設計模式,否則,不少時候,即便咱們用了面嚮對象語言編程,每每出現拔苗助長的效果。spa

 

這裏先給出面向對象五大基本原則:設計

(DIP)依賴倒置原則:3d

  1. 高層模塊(穩定)不該該依賴於底層模塊(變化),兩者都應該依賴於抽象(穩定)。code

  2. 抽象(穩定)不該該依賴於實現細節(變化),實現細節依賴於抽象(穩定)。對象

  個人理解:總結爲,穩定的東西不該該依賴於變化的東西。在面向對象設計過程當中,可能有時候,我會把一些具體實現依賴另一些具體實現,可是咱們知道,具體實現是容易變化的,若是依賴的實現發現變化,意味着上層也將發生變化。這裏的變化,不是說具體的邏輯變更,而是接口變更,簡單的說就是,面向對象應該面向抽象接口來實現,而不是面向具體的邏輯,這個抽象接口應該是十分穩定的,依賴於一個穩定的接口的好處,就是,咱們具體實現和耦合性會比較低。這意味着咱們須要很敏感的發現系統的穩定點和變化點,將穩定點變成抽象接口,把變化點變成擴展實現。blog

我本身想了一個壞栗子,不知道合不合適:

class Waiter //服務生
{


}

class Cater //計算員
{

}

class Guard //保安
{

}
class Bank
{
public:
  vector<Waiter> waiterList;
  vector<Cater> caterList;
  vector<Guard> guardList;
  
protect:
  void doWork();  
}

void Bank::doWord()
{
for(auto a in waiterList)
{
a.doWord();
}
for(auto a in caterList)
{
a.doWord();
}
for(auto a in guardList)
{
a.doWord();
}

做爲僞代碼,隨意看看吧,我想意思很簡單,首先有個員工文件,裏面我定義了三種員工,而後銀行定義了員工的LIst,固然,實際可能還須要添加員工犯法,dowork方法用來讓他的員工工做,代碼冗餘問題雖然還能夠解決,可是,最大的問題是,你應該發現,員工種類咱們假設是須要擴展的,這種方式,就是具體依賴於具體。你能夠假設若是如今多了一種員工,叫作數據庫管理員,那麼你對於上面的代碼應該作那些修改。我想你應該,寫一個類,而後,在銀行類裏面,添加一個數據庫管理員list對象,最後,還要在員工在工做裏面在添加方法。是否是很不利。

我想,好的方式是這樣:

 

class Staff
{
public:
    void doWork()=0;
    void ~Staff()
    {}
}

class Waiter:public Staff //服務生
{
public:
    void doWork();

}

class Cater:public Staff //計算員
{
    void doWork();
}

class Guard:public Staff //保安
{
    void doWork();
}

 

 

class Bank
{
public:
 vector<Staff *> stafflList;
  
protect:
  void doWork();  
}

void Bank::doWord()
{
for(auto a in staffList)
{
   a.doWork();
}

 

 你如今能夠想象下,若是這種狀況下,老闆讓你添加一種新的員工,叫作數據庫,管理員,那麼是否是方便,你只須要,在寫一個類,而後繼承抽象基類staff便可。這也就是面向抽象基礎編程。

開放封閉原則(OCP)

  1. 對擴展開放,對更改封閉。

  2. 類模塊應該可擴展的,可是不可更改。

個人理解:開放封閉原則,是最爲重要的設計原則。然後面的Liskov替換原則和合成/聚合複用原則爲開放封閉原則的實現提供保證。

我這裏無恥的引用百度百科的例子:

class BusyBankStaff
{
    private BankProcess bankProc = new BankProcess();
// 定義銀行員工的業務操做
    public void HandleProcess(Client client)
    {
        switch (client.ClientType)
        {
            case "存款用戶":
                bankProc.Deposit();
               break;
             case "轉帳用戶":
                bankProc.Transfer();
               break;
             case "取款戶":
                bankProc.DrawMoney();
                break;
        }
    }
}

其實這代碼跟第一個原則差很少,對擴展開發怎麼理解,這段代碼問題主要在於,若是有新的業務出現,那麼擴展,將在switch中進行修改,實際上違背了對修改封閉的原則,也就是說,若是出現新的業務,咱們須要的是擴展,而不是修改以前的類。那麼好的實現方法能夠是這樣的:

class IBankProcess
{
    virtual void Process()=0;
}

<-接口基類

class DepositProcess : IBankProcess
{
//IBankProcess Members

    public void :
    Process()
    {
    // 辦理存款業務
    }
}
class TransferProcess : IBankProcess
{
//IBankProcess Members

    public :
    void Process()
    {
    // 辦理轉帳業務

    }
}
class DrawMoneyProcess : IBankProcess
{
//IBankProcess Members

    public :

    void Process()
    {
    // 辦理取款業務 
    }
}

<-不一樣業務

 

class EasyBankStaff
{
    private :
        IBankProcess *bankProc = NULL;
    public :
    void HandleProcess(Client client)
    {
         bankProc = client.CreateProcess();
         bankProc.Process();
    }
}

<-用戶切換

 

class BankProcess
{
    public:
    void Main()
    {
        EasyBankStaff bankStaff = new EasyBankStaff();
        bankStaff.HandleProcess(new Client("轉帳用戶"));
    }
}

處理業務,就這個方法而言,對於銀行業務擴展,咱們只須要定義新的類,這叫對擴展開放,而不須要修改原先類的代碼,這叫對修改封閉。這樣一個原則,是否是很像一個衡量標準呢。

 

單一職責原則(SRP)

  1. 一個類應該僅有一個引發它變化的緣由。

  2. 變化的方向隱含着類的責任。

這是相對比較好理解的吧,"若是一個類承擔的職責過多,就等於把這些職責耦合在一塊兒了。一個職責的變化可能會削弱或者抑制這個類完成其餘職責的能力。這種耦合會致使脆弱的設計,當發生變化時,設計會遭受到意想不到的破壞。而若是想要避免這種現象的發生,就要儘量的遵照單一職責原則。此原則的核心就是解耦和加強內聚性。"

一個類有且只有一個能夠改變的理由。

 

 

liskov替換原則(LSP)

  1. 子類必須可以替換它們的基類(IS-A)。

  2. 繼承表達類型抽象。

 

接口隔離原則(ISP)

  1. 不能夠強迫客戶程序依賴他們不用的方法。

  2. 接口應該小而完備。

 

 

幾個額外的建議:

組合複用原則(carp):

優先使用對象組合,而不是類繼承

  1. 類繼承一般爲「白箱複用」,對象組合一般爲「黑箱複用」。

  2. 繼承在某種程度上破壞了封裝性,之類父類耦合度高。

  3. 而對象組合則只要求被組合的對象具備良好定義的接口,耦合度低

 

封閉變化點

    使用封裝來建立對象之間的分界層。讓設計者能夠在變化一側進行修改,而不會影響另外一側產生不良影響,從而實現鬆耦合。

 

針對接口編程

   針對接口編程,而不是針對實現編程

    1.不將變量類型聲明爲某個特定的具體類,而是聲明爲某個接口

    2.客戶程序不須要獲知對象的具體類型,只須要知道對象所具備的接口。

    3.減小系統中各部分的依賴關係,從而實現「高內聚,鬆耦合」的設計類型。

對設計模式按照目的分類能夠分爲如下三種:

1.建立型:與對象建立有關。

2.結構型:處理類或對象的組合。

3.行爲型:模式對類或者對象怎麼交互和分配職責進行描述。

按照其做用範圍又可分爲類模式對象模式 ,前者主要處理類和子類的關係,經過繼承創建,具備靜態,在編譯時刻就穩定下來。後者處理對象之間的關係,這些關係多是運行時刻變化的,具備動態性。

具體模式能夠參考書上的表格:

 

如何使用設計模式:

我想大概是要達到對症下藥的層次吧,在此以前,咱們更應該關注原則。

相關文章
相關標籤/搜索