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原则,可以显著提高代码的可维护性、可扩展性和灵活性,使开发和维护变得更加高效。

相关推荐
IT女孩儿20 分钟前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript
m0_7482389221 分钟前
webgis入门实战案例——智慧校园
开发语言·ios·swift
Clockwiseee34 分钟前
PHP伪协议总结
android·开发语言·php
小灰灰搞电子36 分钟前
Qt实现Android的图案密码(图形解锁)源码分享
开发语言·qt
吴冰_hogan2 小时前
JVM(Java虚拟机)的组成部分详解
java·开发语言·jvm
白宇横流学长3 小时前
基于java出租车计价器设计与实现【源码+文档+部署讲解】
java·开发语言
数据小爬虫@5 小时前
Java爬虫实战:深度解析Lazada商品详情
java·开发语言
songroom5 小时前
Rust: offset祼指针操作
开发语言·算法·rust
code04号5 小时前
C++练习:图论的两种遍历方式
开发语言·c++·图论
煤泥做不到的!6 小时前
挑战一个月基本掌握C++(第十一天)进阶文件,异常处理,动态内存
开发语言·c++