C#的五大设计原则-solid原则

什么是C#的五大设计原则,我们用人话来解释一下,希望小伙伴们能学会:

好的,让我们以一种幽默的方式来解释C#的五大设计原则(SOLID):

单一职责原则(Single Responsibility Principle, SRP):别让一个类做得像瑞士军刀一样样样都干,它会累趴下的。让每个类专注于一件事,成就一个小专家!

开闭原则(Open/Closed Principle, OCP):代码就像房子一样,应该允许你随时装修,但不要让你每次都得拆墙。你的类应该开放给扩展,关闭给修改。装修可以,但别动地基。

里氏替换原则(Liskov Substitution Principle, LSP):如果它看起来像鸭子,游起来像鸭子,叫起来也像鸭子,那它就应该是个鸭子。子类应该能完全替代父类,就像鸭子替代鸭子一样。

接口隔离原则(Interface Segregation Principle, ISP):不要逼用户点一堆不想要的菜,分开菜单让他们点自己喜欢的。让接口精简,用户用起来才不会吐槽你。

依赖倒置原则(Dependency Inversion Principle, DIP):别总想着亲自动手干活,雇个专业的帮你干!高层模块不应该依赖低层模块,而应该依赖于抽象。让专业的人做专业的事,你只需要指挥就好。

1. 单一职责原则(SRP - Single Responsibility Principle)

不使用SRP:

csharp 复制代码
public class UserService
{
    public void RegisterUser(string name, string email)
    {
        // 保存用户到数据库
        SaveUserToDatabase(name, email);

        // 发送欢迎邮件
        SendWelcomeEmail(email);
    }

    private void SaveUserToDatabase(string name, string email)
    {
        // 代码逻辑
    }

    private void SendWelcomeEmail(string email)
    {
        // 代码逻辑
    }
}

在这个示例中,UserService类同时处理用户注册和发送邮件,职责不单一,导致类的复杂性增加,不利于维护和扩展。

使用SRP:

csharp 复制代码
public class User
{
    public string Name { get; set; }
    public string Email { get; set; }
}

public class UserRepository
{
    public void AddUser(User user) { /* 代码 */ }
    public User GetUserByEmail(string email) { /* 代码 */ }
}

public class EmailService
{
    public void SendEmail(User user) { /* 代码 */ }
}

public class UserService
{
    private readonly UserRepository _userRepository;
    private readonly EmailService _emailService;

    public UserService(UserRepository userRepository, EmailService emailService)
    {
        _userRepository = userRepository;
        _emailService = emailService;
    }

    public void RegisterUser(User user)
    {
        _userRepository.AddUser(user);
        _emailService.SendEmail(user);
    }
}

在这个示例中,职责被分离到了不同的类中,UserService只负责协调这些操作,使代码更清晰、可维护。

2. 开闭原则(OCP - Open/Closed Principle)

不使用OCP:

csharp 复制代码
public class Shape
{
    public double Radius { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }

    public double GetArea(string shapeType)
    {
        if (shapeType == "Circle")
        {
            return Math.PI * Radius * Radius;
        }
        else if (shapeType == "Rectangle")
        {
            return Width * Height;
        }
        else
        {
            return 0;
        }
    }
}

在这个示例中,添加新的形状类型需要修改GetArea方法的代码,违反了开闭原则。

使用OCP:

csharp 复制代码
public abstract class Shape
{
    public abstract double Area();
}

public class Circle : Shape
{
    public double Radius { get; set; }
    public override double Area() => Math.PI * Radius * Radius;
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public override double Area() => Width * Height;
}

在这个示例中,通过扩展Shape类,我们可以添加新的形状而不需要修改现有的代码,符合开闭原则。

3. 里氏替换原则(LSP - Liskov Substitution Principle)

不使用LSP:

csharp 复制代码
public class Rectangle
{
    public virtual double Width { get; set; }
    public virtual double Height { get; set; }
    public double Area() => Width * Height;
}

public class Square : Rectangle
{
    public override double Width
    {
        set { base.Width = base.Height = value; }
    }

    public override double Height
    {
        set { base.Width = base.Height = value; }
    }
}

在这个示例中,Square类违反了里氏替换原则,因为WidthHeight的行为在子类中改变了。

使用LSP:

csharp 复制代码
public abstract class Shape
{
    public abstract double Area();
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public override double Area() => Width * Height;
}

public class Square : Shape
{
    public double SideLength { get; set; }
    public override double Area() => SideLength * SideLength;
}

在这个示例中,RectangleSquare都正确实现了ShapeArea方法,符合里氏替换原则。

4. 接口隔离原则(ISP - Interface Segregation Principle)

不使用ISP:

csharp 复制代码
public interface IWorker
{
    void Work();
    void Eat();
}

public class Worker : IWorker
{
    public void Work() { /* 代码逻辑 */ }
    public void Eat() { /* 代码逻辑 */ }
}

public class Robot : IWorker
{
    public void Work() { /* 代码逻辑 */ }
    public void Eat()
    {
        throw new NotImplementedException();
    }
}

在这个示例中,Robot类必须实现它不需要的Eat方法,导致接口不符合实际需求。

使用ISP:

csharp 复制代码
public interface IWorkable
{
    void Work();
}

public interface IEatable
{
    void Eat();
}

public class Worker : IWorkable, IEatable
{
    public void Work() { /* 代码逻辑 */ }
    public void Eat() { /* 代码逻辑 */ }
}

public class Robot : IWorkable
{
    public void Work() { /* 代码逻辑 */ }
}

在这个示例中,IWorkableIEatable接口被分离,Robot类只实现了需要的接口,符合接口隔离原则。

5. 依赖倒置原则(DIP - Dependency Inversion Principle)

不使用DIP:

csharp 复制代码
public class DatabaseLogger
{
    public void Log(string message)
    {
        // 记录日志到数据库
    }
}

public class UserService
{
    private readonly DatabaseLogger _logger = new DatabaseLogger();

    public void RegisterUser(string name, string email)
    {
        // 记录日志
        _logger.Log("Registering user " + name);

        // 其他代码
    }
}

在这个示例中,UserService类直接依赖于具体的DatabaseLogger实现,违反了依赖倒置原则。

使用DIP:

csharp 复制代码
public interface ILogger
{
    void Log(string message);
}

public class DatabaseLogger : ILogger
{
    public void Log(string message)
    {
        // 记录日志到数据库
    }
}

public class FileLogger : ILogger
{
    public void Log(string message)
    {
        // 记录日志到文件
    }
}

public class UserService
{
    private readonly ILogger _logger;

    public UserService(ILogger logger)
    {
        _logger = logger;
    }

    public void RegisterUser(string name, string email)
    {
        // 记录日志
        _logger.Log("Registering user " + name);

        // 其他代码
    }
}

在这个示例中,UserService类依赖于ILogger接口,而不是具体的实现,符合依赖倒置原则,使得代码更加灵活和可扩展。

总结对比

  • 单一职责原则(SRP): 通过分离职责减少类的复杂度,使代码更易读和维护。不使用SRP会导致类变得庞大,职责不清,维护困难。
  • 开闭原则(OCP): 通过扩展来增加新功能,而不是修改已有代码,提高了代码的稳定性和可扩展性。不使用OCP会导致每次增加新功能都需要修改已有代码,增加了出错的风险。
  • 里氏替换原则(LSP): 确保子类可以替代基类而不会导致程序出错,保证继承的正确性。不使用LSP会导致子类行为不一致,破坏程序的稳定性。
  • 接口隔离原则(ISP): 通过细化接口,使得类只依赖于需要的接口,减少不必要的依赖关系。不使用ISP会导致类实现不需要的接口方法,增加了代码的复杂度。
  • 依赖倒置原则(DIP): 通过依赖于抽象(接口或抽象类)而不是具体实现,降低模块之间的耦合,提高代码的灵活性和可测试性。不使用DIP会导致类之间的高耦合,难以进行单元测试和模块替换。

通过遵循SOLID原则,可以显著提高代码的可维护性、可扩展性和灵活性,使开发和维护变得更加高效。

相关推荐
isyangli_blog40 分钟前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008111 小时前
FastAPI APIRouter
开发语言·python
Benszen1 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆1 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木1 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充1 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~1 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6162 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草2 小时前
反射、Tomcat执行
java·开发语言
雪的季节3 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt