编辑
2025-09-18
C#
00

目录

S. 单一职责原则 (SRP)
遵循 SRP 的重构
O. 开闭原则 (OCP)
L. 里氏替换原则 (LSP)
I. 接口隔离原则 (ISP)
D. 依赖倒置原则 (DIP)

软件开发是一门复杂的技艺,常常被比作建筑施工。正如建筑师和工程师遵循原则以确保建筑物的坚固和可维护性,软件工程师也遵循原则来创建健壮、可扩展和可维护的代码。在软件开发社区中,一套被广泛认可的原则是 SOLID,这一缩写是由 Robert C. Martin 提出的。本文将探讨 SOLID 原则,并展示如何在 C# 中应用这些原则。

image.png

S. 单一职责原则 (SRP)

单一职责原则规定一个类应该仅有一个改变的理由,换句话说,一个类应该只承担一种职责。这一原则促进了高内聚和低耦合,使得类更易于理解、维护和测试。

C# 示例

假设我们有一个处理用户信息的类,它同时负责用户的认证和用户数据的操作。

C#
public class UserManager { public void AddUser(string username, string password) { // 添加用户到数据库 } public bool Login(string username, string password) { // 验证用户凭证 return true; } }

在这个例子中,UserManager 类同时负责用户的添加和登录认证。这违反了单一职责原则,因为这个类有两个改变的理由:用户数据管理和用户认证逻辑的变更。

遵循 SRP 的重构

为了遵循单一职责原则,我们可以将 UserManager 类拆分为两个类,每个类负责一个单一的功能。

C#
public class UserAuthentication { public bool Login(string username, string password) { // 验证用户凭证 return true; } } public class UserDataManager { public void AddUser(string username, string password) { // 添加用户到数据库 } }

在这个重构后的设计中,UserAuthentication 类专门负责用户的登录认证,而 UserDataManager 类专门负责用户数据的管理。这样,每个类都只有一个改变的理由,更加符合单一职责原则。

O. 开闭原则 (OCP)

开闭原则规定软件实体(类、模块、函数等)应对扩展开放,对修改关闭。这意味着你应该能够扩展一个模块的行为,而无需修改其源代码。

C# 示例

C#
public abstract class Shape { public abstract void Draw(); } public class Circle : Shape { public override void Draw() { // 绘制圆形 } } public class Square : Shape { public override void Draw() { // 绘制正方形 } } public class GraphicEditor { public void DrawShape(Shape shape) { shape.Draw(); } }

在这个例子中,GraphicEditor 不需要修改就可以支持新的形状类型,符合开闭原则。

L. 里氏替换原则 (LSP)

里氏替换原则规定,程序中的对象应该能够被其子类对象无缝替换,而不会影响程序的正确性。这一原则确保了子类可以代替其超类被使用。

C# 示例

C#
public class Bird { public virtual void Fly() { // 实现飞行 } } public class Penguin : Bird { public override void Fly() { throw new NotImplementedException("企鹅不会飞。"); } }

这违反了 LSP。我们应该重新设计类结构:

C#
public abstract class Bird { } public class FlyingBird : Bird { public virtual void Fly() { // 实现飞行 } } public class Penguin : Bird { }

I. 接口隔离原则 (ISP)

接口隔离原则规定客户端不应该被迫依赖于它们不使用的接口。这一原则鼓励开发者设计精细化的接口,以满足客户的具体需求。

C# 示例

C#
public interface IMachine { void Print(Document d); void Scan(Document d); void Fax(Document d); } public class MultiFunctionPrinter : IMachine { public void Print(Document d) { /* 打印 */ } public void Scan(Document d) { /* 扫描 */ } public void Fax(Document d) { /* 传真 */ } }

为了符合 ISP,我们可以将接口拆分:

C#
public interface IPrinter { void Print(Document d); } public interface IScanner { void Scan(Document d); } public class SimplePrinter : IPrinter { public void Print(Document d) { /* 打印 */ } } public class MultiFunctionMachine : IPrinter, IScanner { public void Print(Document d) { /* 打印 */ } public void Scan(Document d) { /* 扫描 */ } }

D. 依赖倒置原则 (DIP)

依赖倒置原则规定高层模块不应依赖于低层模块,两者都应依赖于抽象。抽象不应依赖于细节,细节应依赖于抽象。

C# 示例

C#
public class BookStore { private MySQLDatabase db = new MySQLDatabase(); public void Add(Book book) { db.Add(book); } } public class MySQLDatabase { public void Add(Book book) { // 将书籍添加到 MySQL 数据库 } }

为了符合 DIP,我们应该引入抽象层:

C#
public interface IDatabase { void Add(Book book); } public class MySQLDatabase : IDatabase { public void Add(Book book) { /* 将书籍添加到 MySQL 数据库 */ } } public class BookStore { private IDatabase db; public BookStore(IDatabase db) { this.db = db; } public void Add(Book book) { db.Add(book); } }

遵循 SOLID 原则可以帮助开发者创建更易于维护、更灵活和更易于理解的软件。虽然在开发初期阶段实施这些原则可能需要额外的努力,但它们最终将导致更好的软件设计并减少长期的技术债务。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!