C#中的頂級設計模式
設計模式是解決常見軟體開發問題的可重用解決方案,提供結構化和實現物件導向程式碼的模板,讓其更有效率和易於維護。 它們幫助開發人員以靈活和可擴展的方式解決物件建立、結構和通信問題。 設計模式作為最佳實踐概念,指導開發人員撰寫更好的程式碼。 軟體設計中的一個基礎原則是單一職責原則(SRP),它是SOLID原則的一部分。
在他的影片"設計模式:在C#中實際解釋單一職責原則(SOLID中的S)"中,Tim Corey 探討了單一職責原則(SRP),強調其在軟體設計中的重要性並提供如何有效實施的實用見解。 本文提供了他影片的要點概述,強調SRP在創造清晰、可維護程式碼中的重要性。
SRP簡介
在軟體設計中,SOLID原則對於建立可維護和可擴展的程式碼至關重要。 它們確保程式碼易於理解、測試和修改。 五個原則—單一職責原則(SRP)、開放關閉原則(OCP)、里氏替換原則(LSP)、接口分離原則(ISP)、依賴反轉原則(DIP)—是物件導向設計的基礎,可在設計模式內應用以使解決方案更加穩健。
通過在C#中應用設計模式,開發人員可以更有效地解決常見問題。 無論是建立物件、定義樹結構,還是確保單一實例的可重用性,設計模式提供預定義的解決方案以增強軟體架構。 像工廠方法、建造者和單例等模式提供靈活、可重用的解決方案,而行為和結構模式有助於管理複雜性並改善系統內部的通信。 通過學習和利用這些模式,開發人員可以構建更易於維護和擴展的系統。
Tim 討論了 SRP 的概念,強調開發人員確保其程式碼符合最佳實踐的重要性。 SRP 指出一個類別應該只有一個職責或變更的理由。 這一原則有助於維持清晰、可維護和可擴展的程式碼。
展示程式碼概覽
Tim 設置了一個簡單的C#控制台應用程式,要求使用者提供名字和姓氏,驗證這些名字,然後生成一個使用者名。 最初的實現違反了SRP,這是一個很好的展示機會如何重構程式碼以符合該原則。
SRP 的解釋
Tim 通過強調初始類中多個職責來解釋 SRP:
- 使用者互動:處理歡迎訊息和提示。
- 資料捕獲:捕獲使用者的名字和姓氏。
- 驗證:驗證輸入名字。
- 使用者名生成:從輸入名中生成使用者名。
這些職責中的每一個都代表類變更的不同原因,違背了SRP。
重構以符合SRP
Tim 示範如何重構程式碼以遵循SRP,將每個職責提取到自己的類中。 這種方法確保每個類只有一個變更理由,使程式碼更模組化並更易於維護。
實際範例
Tim 提供了一個重構的實際範例:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to my application");
Console.Write("Enter your first name: ");
string firstName = Console.ReadLine();
Console.Write("Enter your last name: ");
string lastName = Console.ReadLine();
if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
{
Console.WriteLine("You did not give us valid information!");
Console.ReadLine();
return;
}
var userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower();
Console.WriteLine($"Your username is {userName}");
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
}using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to my application");
Console.Write("Enter your first name: ");
string firstName = Console.ReadLine();
Console.Write("Enter your last name: ");
string lastName = Console.ReadLine();
if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
{
Console.WriteLine("You did not give us valid information!");
Console.ReadLine();
return;
}
var userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower();
Console.WriteLine($"Your username is {userName}");
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
}重構逐步指南
步驟 1:建立 StandardMessages 類
首先,Tim 建立了一個類來處理顯示給使用者的標準訊息。 該類將管理歡迎訊息和結束訊息。
public class StandardMessages
{
public static void WelcomeMessage()
{
Console.WriteLine("Welcome to my application");
}
public static void EndApplication()
{
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
public static void ShowValidationErrorMessage()
{
Console.WriteLine("You did not give us valid information!");
}
}public class StandardMessages
{
public static void WelcomeMessage()
{
Console.WriteLine("Welcome to my application");
}
public static void EndApplication()
{
Console.WriteLine("Press enter to close...");
Console.ReadLine();
}
public static void ShowValidationErrorMessage()
{
Console.WriteLine("You did not give us valid information!");
}
}在 Program 類中,用 StandardMessages 類中的方法替換直接調用 Console.WriteLine 和 Console.ReadLine:
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
// Other code...
StandardMessages.EndApplication();
}
}class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
// Other code...
StandardMessages.EndApplication();
}
}步驟 2:建立 PersonDataCapture 類
接下來,Tim 建立了一個類來處理捕獲使用者的名字和姓氏。 該類將負責收集使用者輸入並返回 Person 物件。
public class PersonDataCapture
{
public static Person Capture()
{
Person output = new Person();
Console.Write("Enter your first name: ");
output.FirstName = Console.ReadLine();
Console.Write("Enter your last name: ");
output.LastName = Console.ReadLine();
return output;
}
}public class PersonDataCapture
{
public static Person Capture()
{
Person output = new Person();
Console.Write("Enter your first name: ");
output.FirstName = Console.ReadLine();
Console.Write("Enter your last name: ");
output.LastName = Console.ReadLine();
return output;
}
}步驟 3:建立 Person 類
您還需要一個 Person 類來保存使用者的名字和姓氏。
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}在 Program 類中,用 PersonDataCapture.Capture 的調用替換直接使用者輸入處理:
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
// Other code...
StandardMessages.EndApplication();
}
}class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
// Other code...
StandardMessages.EndApplication();
}
}步驟 4:建立 PersonValidator 類
接下來,Tim 建立了一個類來處理驗證使用者的名字和姓氏。 該類將負責確保名字不為空或空白。
public class PersonValidator
{
public static bool Validate(Person person)
{
if (string.IsNullOrWhiteSpace(person.FirstName))
{
StandardMessages.ShowValidationErrorMessage("first name");
return false;
}
if (string.IsNullOrWhiteSpace(person.LastName))
{
StandardMessages.ShowValidationErrorMessage("last name");
return false;
}
return true;
}
}public class PersonValidator
{
public static bool Validate(Person person)
{
if (string.IsNullOrWhiteSpace(person.FirstName))
{
StandardMessages.ShowValidationErrorMessage("first name");
return false;
}
if (string.IsNullOrWhiteSpace(person.LastName))
{
StandardMessages.ShowValidationErrorMessage("last name");
return false;
}
return true;
}
}在 Program 類中,用 PersonValidator.Validate 的調用替換驗證程式碼:
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
if (!PersonValidator.Validate(user))
{
StandardMessages.EndApplication();
return;
}
// Other code...
StandardMessages.EndApplication();
}
}class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
if (!PersonValidator.Validate(user))
{
StandardMessages.EndApplication();
return;
}
// Other code...
StandardMessages.EndApplication();
}
}步驟 7:建立 AccountGenerator 類
Tim 將使用者名生成和帳號建立邏輯移到一個新的 AccountGenerator 類中。
建立 AccountGenerator 類:
- 該類包括生成使用者名和模擬帳號建立的邏輯。
public class AccountGenerator { public static void CreateAccount(Person user) { string username = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower(); Console.WriteLine($"Your username is: {username}"); } }public class AccountGenerator { public static void CreateAccount(Person user) { string username = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower(); Console.WriteLine($"Your username is: {username}"); } }更新 Main 類:
- 在主類中調用 AccountGenerator 以建立帳號。
class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
bool isUserValid = PersonValidator.Validate(user);
if (!isUserValid)
{
StandardMessages.EndApplication();
return;
}
AccountGenerator.CreateAccount(user);
StandardMessages.EndApplication();
}
}class Program
{
static void Main(string[] args)
{
StandardMessages.WelcomeMessage();
Person user = PersonDataCapture.Capture();
bool isUserValid = PersonValidator.Validate(user);
if (!isUserValid)
{
StandardMessages.EndApplication();
return;
}
AccountGenerator.CreateAccount(user);
StandardMessages.EndApplication();
}
}
總結與結論
在這一結束部分,Tim Corey 總結了通過重構演示程式碼實施單一職責原則(SRP)的好處。 他強調將應用程式分為更小的專注類的優勢。
SRP 的關鍵優點
簡化的程式碼維護:
每個類都有單一的職責,使得更容易找到需要更改的地方。 例如,使用者資料捕獲邏輯明確放在 PersonDataCapture 下。
- 這種結構簡化了理解,任何想修改使用者驗證的人都知道要檢查 PersonValidator。
提高可讀性:
- 明確定義的職責,使主程式流程更具可讀性。 程式碼現在讀起來像一組清晰的連續操作:
StandardMessages.WelcomeMessage(); Person user = PersonDataCapture.Capture(); bool isUserValid = PersonValidator.Validate(user); if (!isUserValid) { StandardMessages.EndApplication(); return; } AccountGenerator.CreateAccount(user); StandardMessages.EndApplication();StandardMessages.WelcomeMessage(); Person user = PersonDataCapture.Capture(); bool isUserValid = PersonValidator.Validate(user); if (!isUserValid) { StandardMessages.EndApplication(); return; } AccountGenerator.CreateAccount(user); StandardMessages.EndApplication();減少複雜性:
負責任務集中的小類通常程式碼行更少,更易於理解和維護。
- 例子:StandardMessages 類的方法簡潔並具有單一用途,例如顯示歡迎訊息或結束應用程式。
public class StandardMessages { public static void WelcomeMessage() { Console.WriteLine("Welcome to my application"); } public static void EndApplication() { Console.WriteLine("Press enter to close..."); Console.ReadLine(); } public static void ShowValidationErrorMessage(string fieldName) { Console.WriteLine($"You did not give us a valid {fieldName}!"); } }public class StandardMessages { public static void WelcomeMessage() { Console.WriteLine("Welcome to my application"); } public static void EndApplication() { Console.WriteLine("Press enter to close..."); Console.ReadLine(); } public static void ShowValidationErrorMessage(string fieldName) { Console.WriteLine($"You did not give us a valid {fieldName}!"); } }
易於程式碼更改:
由於每個類只有單一的變更原因,因此根據新要求修改程式碼變得簡單明瞭。
- 例子:如果要求是更改結束訊息,則更改僅發生在 StandardMessages.EndApplication 方法中。
更好的除錯和協作:
隨著小而明確的類,除錯變得簡單,因為您可以輕鬆指出問題的位置。
- 新開發者可以更快地融入,理解每個類的清晰結構和職責。
應對許多類的問題
Tim 針對一個常見問題,即應用 SRP 導致太多類,使項目變得繁重:
導航和理解:
像 Visual Studio 中的 IntelliSense 這樣的工具讓導航多個類變得簡單。 例如,按 F12 可直接導航到方法或類的定義。
- 擁有許多小的、可管理的部分,比起大的單一類,也許使理解整個應用更容易。
性能和儲存:
- 鑑於現代儲存及計算能力,額外的類並不會顯著影響磁盤空間或性能。
平衡與過度:
Tim 建議尋找平衡。 如果一個類的職責使其增長過大,考慮它是否有多個變更理由,表示可能需要進一步的拆分。
- 他建議如果您需要在 Visual Studio 中大量滾動查看一個類,該類可能過大需要分割。
實際實施
Tim 鼓勵開發人員逐步應用 SRP,尤其是在現有程式碼庫中。 從小變化和新程式碼開始以符合 SRP 原則。 這種漸進的方法確保更順利的過渡和持續改進。
結論
Tim Corey 的重構範例展示了如何通過遵循單一職責原則(SRP)來獲得更清晰、更可維護的程式碼。 通過將職責分解為更小、更專注的類,開發人員可以在其程式碼庫中提高可讀性、除錯能力和協作性。 這一SOLID設計模式的基礎原則為軟體開發中更高級的原則和最佳實踐鋪平了道路。

