1. 程式人生 > 實用技巧 >使用簡單的c#示例的堅實的架構原則

使用簡單的c#示例的堅實的架構原則

內容 固體是什麼?理解“S”- SRP(單一責任原則)理解“O”——開閉原則的理解“L”——太陽能發電(Liskov替換原則)瞭解“我”- ISP(介面隔離原則)理解“D”——依賴性倒置原則。修改堅實的原則 介紹 我知道有1000年的關於這個話題的文章,每個月10新文章在固體會注入更多。我寫這篇文章的目標是理解固體與簡單的c#示例。 任何改進本文評論框下面是開放的。 固體是什麼? 固體有五個基本原則whichhelp建立良好的軟體架構。固體是一個縮寫:- S代表SRP(單一責任原則o代表(OCP(開閉原則)l代表太陽能發電(Liskov替換原則),我代表ISP(介面隔離原則)d代表下降(依賴性倒置原則) 讓我們開始瞭解每個原理簡單的c#示例。 理解“S”- SRP(單一責任原則) 理解固體的最好方法是通過理解它試圖解決什麼問題。看一看下面的程式碼,你能猜出問題是什麼?(你不會讓啤酒猜這J,因為太簡單)。 好吧,讓我給一個提示看catch塊程式碼。 隱藏,複製Code

class Customer
    {
        public void Add()
        {
            try
            {
                // Database code goes here
            }
            catch (Exception ex)
            {
                System.IO.File.WriteAllText(@"c:\Error.txt", ex.ToString());
            }
        }
    }

以上客戶類做他不應該做的事。客戶類客戶datavalidations應該做的,叫客戶資料訪問層等,但如果你看到catch塊緊密也做日誌記錄活動。在簡單的單詞含有很多的責任。 所以明天如果新增一個新的日誌事件檢視器我需要去改變“客戶”類,這是非常奇怪的。 就像“約翰”有一個問題,為什麼我需要檢查“BOB”。 這也讓我想起了著名的瑞士刀。如果其中一個需要更改整個集需要被打擾。不進攻我的鐵桿粉絲,瑞士刀。 但如果我們可以有這些物品分離它的簡單、易於維護和改變不影響另一個。同樣的原則也適用於在軟體架構類和物件。 所以SRP說一個類應該只有一個責任,而不是多個。如果我們應用SRP,日誌記錄活動轉移到其他類誰只會照顧日誌活動。 隱藏,複製Code

class FileLogger
    {
        public void Handle(string error)
        {
            System.IO.File.WriteAllText(@"c:\Error.txt", error);
        }
    }

現在客戶類可以幸福的日誌記錄活動委託給“FileLogger”類,他可以專注於客戶相關的活動。 隱藏,複製Code

class Customer
    {
        private FileLogger obj = new FileLogger();
        publicvirtual void Add()
        {
            try
            {
                // Database code goes here
            }
            catch (Exception ex)
            {
                obj.Handle(ex.ToString());
            }
        }
    }

現在建築的思維過程是一個進化。對於一些老年人的人看著上面SRP例子反駁,甚至可以嘗試捕捉不應該由客戶類,因為這不是他的工作。 是的,我們可以建立一個全域性錯誤處理程式必須在全球。asax檔案,假設您正在使用ASP。淨和處理這些部分的錯誤,使客戶類完全免費。 所以我離開你可以走多遠,會讓這個解決方案更好的但是現在我想保持這個簡單,讓你的思想把它很大程度的自由。 下面是一個偉大的評論,談到如何藉此SRP的例子。 http://www.codeproject.com/Articles/703634/SOLID-architecture-principles-using-simple-Csharp?msg=4729987#xx4729987xx 理解“O”——開閉原則 讓我們繼續我們的客戶類示例相同。我添加了一個簡單的客戶型別屬性的類。這個屬性決定如果這是一個“黃金”奧拉“銀”的客戶。 根據計算相同的折扣。看一看“getDiscount”函式,返回相應的折扣。1金客戶和2銀客戶。 想,有什麼問題下面的程式碼。哈哈哈,看來這篇文章會讓你猜冠軍;-)。 好,還讓我新增一個提示,看這個“如果”條件“getDiscount”功能。 隱藏,複製Code

class Customer
{
        private int _CustType;

        public int CustType
        {
            get { return _CustType; }
            set { _CustType = value; }
        }

        public double getDiscount(double TotalSales)
        {
                if (_CustType == 1)
                {
                    return TotalSales - 100;
                }
                else
                {
                    return TotalSales - 50;
                }
        }
}

問題是如果我們新增一個新客戶型別,我們需要去新增一個“如果”條件“getDiscount”功能,換句話說,我們需要改變客戶類。 如果我們改變一次又一次的客戶類,我們需要確保前面的條件與新再次測試,現有客戶的引用這個類是正常工作。 換句話說我們是“修改”當前的客戶程式碼對於每一個變化,我們每次修改之前我們需要確保所有的功能和聯絡客戶工作。 而不是“修改”我們如何去“擴充套件”。換句話說每次新增新客戶型別需要我們建立一個新類,如下所示。所以無論當前的程式碼,我們只需要測試和檢查新類。 隱藏,複製Code

class Customer
{
        public virtual double getDiscount(double TotalSales)
        {
            return TotalSales;
        }
}

  class SilverCustomer : Customer
    {
        public override double getDiscount(double TotalSales)
        {
            return base.getDiscount(TotalSales) - 50;
        }
    }

藏e,複製Code

class goldCustomer : SilverCustomer
    {
        public override double getDiscount(double TotalSales)
        {
            return base.getDiscount(TotalSales) - 100;
        }
    }

簡單地說,對於任何新的修改,“Customer”類現在都是關閉的,但是當新的客戶型別被新增到專案中時,它對於擴充套件是開啟的。 理解“L”- LSP (Liskov置換原理) 讓我們繼續同一個客戶。假設我們的系統要計算查詢折扣。現在詢盤不是真正的客戶,他們只是線索。因為它們只是線索,所以我們暫時不希望將它們儲存到資料庫中。 因此,我們建立一個名為Enquiry的新類,它繼承自“Customer”類。我們對查詢提供一些折扣,以便它們可以轉換為實際客戶,我們覆蓋了“新增”方法,但有一個例外,以便沒有人可以向資料庫新增查詢。 隱藏,複製Code

class Enquiry : Customer
    {
        public override double getDiscount(double TotalSales)
        {
            return base.getDiscount(TotalSales) - 5;
        }

        public override void Add()
        {
            throw new Exception("Not allowed");
        }
    }

如果您將當前客戶繼承層次結構視覺化,它看起來如下所示。換句話說,“Customer”是父類,“Gold”、“Silver”和“Enquiry”是子類。 因此,根據多型性規則,我的父“客戶”類物件可以指向任何it子類物件,即“金”,“銀”或“查詢”在執行時沒有任何問題。 例如,在下面的程式碼中,您可以看到我建立了一個“Customer”的列表集合,並且多虧了多型性,我可以毫無問題地將“Silver”、“Gold”和“Enquiry”Customer新增到“Customer”集合中。 由於多型,我還可以使用父Customer物件瀏覽“Customer”列表,並呼叫“Add”方法,如下面的程式碼所示。 現在再一次讓我來逗你們,這裡有一個小問題,想,想,想。 提示:-觀察查詢物件在“FOR EACH”迴圈中瀏覽和呼叫的時間。 , 隱藏,複製Code

List<Customer> Customers = new List<Customer>();
Customers.Add(new SilverCustomer());
Customers.Add(new goldCustomer());
Customers.Add(new Enquiry());

 foreach (Customer o in Customers)
 {
                o.Add();
 }
}

根據繼承層次結構,“Customer”物件可以指向它的任何子物件,我們不期望出現任何異常行為。 但是當" Enquiry "物件的" Add "方法被呼叫時,它會導致以下錯誤,因為我們的" Equiry "物件將查詢儲存到資料庫中,因為它們不是實際的客戶。 現在正確地閱讀下面的段落來理解這個問題。如果你不明白下面的段落,請讀兩遍。 換句話說,“詢價”有折扣計算,它看起來像一個“客戶”,但它不是一個客戶。因此父物件不能無縫地替換子物件。換句話說,“Customer”不是“Enquiry”類的實際父類。“查詢”是一個完全不同的實體。 所以LISKOV原則說父物件應該很容易替換子物件。因此,要實現LISKOV,我們需要建立兩個介面,一個用於折扣,另一個用於資料庫,如下所示。 隱藏,複製Code

interface IDiscount
{
        double getDiscount(double TotalSales);
}


interface IDatabase
{
        void Add();
}

現在,“Enquiry”類將只實現“IDiscount”,因為它對“Add”方法不感興趣。 隱藏,複製Code

class Enquiry : IDiscount
    {
        public  double getDiscount(double TotalSales)
        {
            return TotalSales - 5;
        }
    }

而“Customer”類將同時實現“IDiscount”和“IDatabase”,因為它還希望將客戶持久化到資料庫中。 隱藏,複製Code

class Customer : IDiscount, IDatabase
   {


       private MyException obj = new MyException();
       public virtual void Add()
       {
           try
           {
               // Database code goes here
           }
           catch (Exception ex)
           {
               obj.Handle(ex.Message.ToString());
           }
       }

       public virtual double getDiscount(double TotalSales)
       {
           return TotalSales;
       }
   }

現在沒有混淆了,我們可以建立一個“Idatabase”介面列表,並向其中新增相關的類。如果我們錯誤地將“Enquiry”類新增到列表中,編譯器會報錯,如下面的程式碼片段所示。 , 理解“I”- ISP(介面分離原則) 現在假設我們的customer類已經成為一個超級熱門元件,它在1000個客戶機上使用,它們都非常喜歡使用customer類。 , 現在讓我們假設一些新客戶提出一個要求,說我們也想要一個方法來幫助我們“讀取”客戶資料。因此,高度熱情的開發人員會希望更改“IDatabase”介面,如下所示。 但你能猜到,我們這樣做是做了一件可怕的事情嗎? 提示:-考慮一下這個變化對上面圖片的影響。 隱藏,複製Code

interface IDatabase
{
        void Add(); // old client are happy with these.
voidRead(); // Added for new clients.
}

如果你設想新的要求已經出現,你有兩種客戶的:- 想要的就用“新增”方法。另一個人想用“新增”+“閱讀”。 現在,通過更改當前介面,您正在做一件可怕的事情,即使當客戶端對“Read”方法不感興趣時,也會干擾1000個滿意的當前客戶端。你強迫他們使用“閱讀”方法。 因此,更好的方法是將現有客戶留在他們自己的甜蜜世界中,並分別為新客戶提供服務。 因此,更好的解決方案是建立一個新介面,而不是更新當前介面。因此,我們可以保持當前介面“IDatabase”不變,並使用“Read”方法新增一個新介面“IDatabaseV1”,“V1”代表版本1。 隱藏,複製Code

interface IDatabaseV1 : IDatabase // Gets the Add method
{
Void Read();
}

現在,您可以建立實現“Read”方法的新類,並滿足新客戶端的需求,而舊客戶端對沒有“Read”方法的舊介面保持原樣並感到滿意。 隱藏,複製Code

class CustomerwithRead : IDatabase, IDatabaseV1
    {

public void Add()
{
	Customer obj = new Customer();
Obj.Add();
}
	Public void Read()
	{
	// Implements  logic for read
}
    }

所以老客戶端將繼續使用“IDat”當新客戶端可以使用“IDatabaseV1”介面時。 隱藏,複製Code

IDatabase i = new Customer(); // 1000 happy old clients not touched
i.Add();

IDatabaseV1 iv1 = new CustomerWithread(); // new clients
Iv1.Read();

理解“D”依賴倒置原則 如果您還記得的話,在我們的customer類中,我們建立了一個logger類來滿足SRP。接下來,我們假設建立了新的Logger風味類。 隱藏,複製Code

class Customer
    {
        private FileLogger obj = new FileLogger();
        public virtual void Add()
        {
            try
            {
                // Database code goes here
            }
            catch (Exception ex)
            {
                obj.Handle(ex.ToString());
            }
        }
    }

為了控制事情,我們建立了一個公共介面,並使用這個公共介面將建立新的日誌程式風格。 隱藏,複製Code

interface ILogger
{
        void Handle(string error);
}

下面是三種logger口味,還可以新增更多。 隱藏,複製Code

class FileLogger : ILogger
    {
        public void Handle(string error)
        {
            System.IO.File.WriteAllText(@"c:\Error.txt", error);
        }
    }

隱藏,複製Code

class EverViewerLogger : ILogger
    {
        public void Handle(string error)
        {
            // log errors to event viewer
        }
    }

隱藏,複製Code

class EmailLogger : ILogger
  {
      public void Handle(string error)
      {
          // send errors in email
      }
  }

現在,根據配置設定,不同的日誌記錄器類將在給定時刻使用。因此,為了達到同樣的目的,我們保持了一個簡單的IF條件,它決定使用哪個日誌記錄器類,參見下面的程式碼。 小測驗時間到了,這裡的問題是什麼? 提示:-觀看捕獲塊程式碼。 隱藏,複製Code

class Customer : IDiscount, IDatabase
    {
        private IException obj; 

public virtual void Add(int Exhandle)
        {
            try
            {
                // Database code goes here
            }
            catch (Exception ex)
            {
                if (Exhandle == 1)
                {
                    obj = new MyException();
                }
                else
                {
                    obj = new EmailException();
                }
                obj.Handle(ex.Message.ToString());
            }
        }

上面的程式碼再次違反了SRP,但這一次方面有所不同,它是關於決定應該建立哪些物件。現在,決定建立哪些例項不是“Customer”物件的工作,他應該只關注與Customer類相關的功能。 如果你仔細觀察,最大的問題是“新的”關鍵字。他對需要建立的物件承擔額外的責任。 因此,如果我們把這個責任倒置或委託給其他人,而不是客戶類來做,這將在一定程度上真正解決問題。 , 下面是實現了反轉的修改程式碼。我們已經打開了建構函式的口,我們希望其他人來傳遞物件,而不是由customer類來做。因此,現在由使用customer物件的客戶機負責決定注入哪個Logger類。 隱藏,複製Code

class Customer : IDiscount, IDatabase
 {
        private Ilogger obj;
        public Customer(ILogger i)
        {
            obj = i;
        }
}

因此,現在客戶端將注入Logger物件,並且客戶物件現在不再受那些決定注入哪個Logger類的IF條件的影響。這是實體依賴倒置原則中的最後一個原則。 客戶類將依賴物件的建立委託給使用依賴物件的客戶,從而使客戶類集中於其工作。 隱藏,複製Code

IDatabase i = new Customer(new EmailLogger());

修改堅實的原則 S代表SRP(單一責任原則):-一個類應該只負責一個責任。 O代表OCP(開閉原則):-擴充套件應該優先於修改。 L代表LSP (Liskov substitution principle):-在執行時多型期間,父類物件應該能夠無縫地引用子物件。 I代表ISP(介面隔離原則):——如果客戶端不需要介面,就不應該強迫它使用。 D代表DIP(依賴倒置原則):-高層模組不應該依賴於低層模組,而應該依賴於抽象。 如果您已經完成了這篇文章,那麼接下來的邏輯步驟將是學習GOF設計模式,這裡有一篇同樣的文章,希望您喜歡。 一旦你完全理解了,下一步就是學習設計模式。下面是一個很好的視訊,教你在8小時內設計一個專案的模式。 本文轉載於:http://www.diyabc.com/frontweb/news2082.html