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創建了一個類來處理名字和姓氏的驗證。 此類將負責確保名字不是null或空白。
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}"); } } -
更新主類:
- 在主類中調用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設計模式的這一基礎原則為軟體開發中的更高級原則和最佳實踐鋪平了道路。
