C# 顶级设计模式
设计模式是针对常见软件开发问题的可重复使用的解决方案,提供了以更高效和可维护的方式构造和实现面向对象代码的模板。 这些工具以灵活、可扩展的方式帮助开发人员解决对象创建、结构和通信方面的问题。 设计模式是指导开发人员编写更好代码的最佳实践概念。 软件设计的基本原则之一是单一责任原则 (SRP),它是 SOLID 原则的一部分。
在他的视频"设计模式:单一责任原则在 C# 中的实用解释(SOLID 中的 S)"中,Tim Corey 探索了 Single Responsibility Principle (SRP),强调了它在软件设计中的意义,并就如何有效实现它提供了实用的见解。 本文简明扼要地概述了视频中的主要内容,强调了 SRP 在创建简洁、可维护代码方面的重要性。
SRP 简介
在软件设计中,SOLID 原则对于创建可维护和可扩展的代码至关重要。 他们要确保代码易于理解、测试和修改。 五项原则--单一责任原则 (SRP)、开放/封闭原则 (OCP)、利斯科夫替代原则 (LSP)、接口隔离原则 (ISP) 和依赖反转原则 (DIP)--是面向对象设计的组成部分,可应用于设计模式中,使解决方案更加健壮。
通过应用 C# 中的设计模式,开发人员可以更有效地解决常见问题。 无论是创建对象、定义树形结构,还是确保单一实例的可重用性,设计模式都提供了可增强软件架构的预定义解决方案。 Factory Method、Builder 和 Singleton 等模式提供了灵活、可重用的解决方案,而行为和结构模式则有助于管理复杂性并改善系统内的交流。 通过学习和利用这些模式,开发人员可以构建更易于维护和扩展的系统。
Tim 讨论了 SRP 的概念,强调开发人员必须确保他们的代码符合最佳实践。 SRP 规定,一个类只能有一个需要更改的责任或理由。 这一原则有助于保持代码的简洁、可维护性和可扩展性。
演示代码概述
Tim 用 C# 编写了一个简单的控制台应用程序,该程序会询问用户的姓和名,验证这些姓名,然后生成一个用户名。 最初的实现违反了 SRP,这为演示如何重构代码以遵守这一原则提供了绝佳的机会。
SRP 解释
Tim 在解释 SRP 时强调了初始类中的多种职责:
1.用户交互:处理欢迎信息和提示。 2.数据捕捉:捕捉用户的姓和名。 3.验证:验证输入名称。 4.用户名生成:根据输入名称生成用户名。
上述每项职责都代表了不同的原因,需要对该类进行更改,这违反了 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!");
}
}在程序类中,将直接调用 Console.WriteLine 和 Console.ReadLine 替换为调用 StandardMessages 类中的方法:
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 类来保存用户的姓和名。
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; }
}在程序类中,用调用 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;
}
}在程序类中,将验证代码替换为调用 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 类中。
1.创建 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}"); } }
1.更新主类:
- 调用 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 的主要优点
1.简化代码维护:
每个类都有单一的责任,这样可以更容易地找到需要修改的地方。 例如,用户数据捕获逻辑被明确置于 PersonDataCapture 下。
- 这种结构简化了理解,因为任何想要修改用户验证的人都知道要查看 PersonValidator。
2.提高可读性:
有了明确的职责,主程序流程就变得更加易读。 现在,代码读起来就像一组清晰、有序的操作:
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();
3.降低复杂性:
责任集中的小班往往代码行数较少,因此更易于理解和维护。
举例说明: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}!"); } }
1.易于修改代码:
由于每个类都有一个需要更改的理由,因此根据新的要求修改代码就变得简单明了。
- 举例说明:如果要求更改结束信息,则仅在 StandardMessages.EndApplication 方法中进行更改。
2.更好的调试和协作:
有了更小的、定义明确的类,调试就会变得更简单,因为您可以很容易地找到问题的位置。
- 新开发人员可以更快地入门,了解每个类的清晰结构和职责。
解决许多类的问题
Tim 解决了一个常见问题,即应用 SRP 会导致类过多,从而使项目变得繁琐:
1.导航和理解:
Visual Studio 中的 IntelliSense 等工具使多个类的导航变得简单明了。 例如,按 F12 键可直接导航到方法或类的定义。
- 与大型单体类相比,拥有许多小的、可管理的片段可以使理解整个应用程序变得更容易。
2.性能和存储:
- 考虑到现代存储和计算能力,额外的类不会对磁盘空间或性能产生重大影响。
3.平衡与过剩:
Tim 建议找到一个平衡点。 如果一个类的责任导致其过于庞大,请考虑它是否有多种变化的原因,这表明它可能需要进一步划分。
- 他建议,如果您需要在 Visual Studio 中大量滚动浏览一个类,那么这个类可能太大,需要拆分。
实际实施
Tim 鼓励开发人员逐步应用 SRP,尤其是在现有代码库中。 从小改动和新代码开始,以符合 SRP 原则。 这种循序渐进的方法可确保顺利过渡和持续改进。
结论
Tim Corey 的重构示例展示了坚持单一责任原则 (SRP) 如何使代码更简洁、更易维护。 通过将职责分解为更小、更集中的类别,开发人员可以提高代码库的可读性、调试和协作能力。 SOLID 设计模式的这一基本原则为软件开发中更高级的原则和最佳实践铺平了道路。

