为了账号安全,请及时绑定邮箱和手机立即绑定

解读SOLID设计原则!

单一职责原则 🗂️ 是面向对象编程中的五个设计原则,帮助开发者创建更易于维护、更具灵活性和可扩展性的软件。

这些原则主要关注什么?就是提升软件设计质量,并提倡最佳实践。

所以 SOLID 原则实际上是由五个原则组成的,每个原则的首字母组成了“SOLID”。
让我们来了解一下吧!

1- S => 单一职责准则 (SRP)
2- O => 开闭原则(也称为“开闭性原则”) (OCP)
3- L => 里氏替换原则 (LSP)
4- I => 接口分离原则 (ISP)
5- D => 依赖反转原则 (DIP)

这些就是五项原则,现在是时候一个个来看了。

……

1. 单一职责原则
  • 定义是:一个类应当只有一个变化的理由,也就是说,它应该只负责一个功能职责。

  • 好处是:通过让每个类只负责单一功能来简化代码并让代码更易于维护。

如果你觉得不对劲的话,看看下一个例子,否则直接跳过它

//违反单一责任原则
public class UserManager {

   public void AddUser(string email , int Id) {
     //一些代码...
   }

   public void SendEmailToUser(int Id) {
     //一些代码...
   }

   public void SendReportToUser(string email) {
     //一些代码...
   }
}

进入全屏 退出全屏

Q1:你觉得我们应该怎么做才能遵循SR(单一职责)原则?

最后一个例子使用了一个 UserManager 类来实现多种功能(增加用户、发送邮件到用户、发送报告到用户)。为了遵循SRP(单一职责原则),每个类应该只负责一个功能。

    //按照单一职责原则

    public class UserManager {
      public void AddUser(string Email, int Id) {
        //待定的代码
      }
    }

    public class EmailService {
      public void SendEmail(int Id) {
        //待定的代码
      }
    }

    public class ReportService {
      public void SendReport(string Email) {
        //待定的代码
      }
    }

点击全屏模式,点击退出全屏


开放封闭原则 (OCP):一个软件实体应该对扩展开放,对修改关闭
  • 定义如下:软件实体(如类、模块、函数)应该易于扩展,却不可修改。因此,开放扩展,但封闭修改,即在不修改原代码的前提下增加新的功能。

  • 好处:可以添加新的功能而不会影响现有代码,从而减少引入错误的风险

我们来看一个违反 OCP 的例子

    public class PaymentService {

      public void 处理付款(string 支付类型) {

         if(支付类型 == "PayPal") {
             //一些代码...
         }  

         if(支付类型 == "CreditCard") {
             //一些代码...
         }  

         if(支付类型 == "比特币") {
             //一些代码...
         }  
      }
    }

全屏,退出全屏

Q2:你觉得最后一个例子违背了开放封闭原则(OCP)吗?

我们现在开始重构代码,使其遵循开闭性原则(OCP)。

    // #1: 创建一个接口
    public interface IPaymentService {
      void process();
    }

    // #2: 实现具体的支付类
    public class PayPalPayment : IPaymentService {
      public void process() {
        //一些具体的代码..
      }
    }

    public class CreditCardPayment : IPaymentService {
      public void process() {
        //一些具体的代码..
      }
    }

切换到全屏模式 退出全屏

就这样,我们让代码遵循了OCP。如何做到的?!
在实现OCP之前,我们不得不在代码中添加一个if语句来检查支付类型。重构之后,遵循了OCP,我们只需添加一个处理支付类型的类,而不需要修改现有的代码!

public class BitcoinPayment : IPaymentService { 

   public void Process() {
     //一些示例代码...
   }

}

点击这里全屏, 点击这里退出全屏


3.里氏替换原则(LSP)
  • 定义:超类的对象可以被子类的对象替换,而不影响程序的正常运行。简单来说,比如说,你有一个叫[A]的超类和一个叫[B]的子类,你就可以在任何使用[A]的地方用[B]来代替。

  • 好处:核心代码在添加外部代码或功能时不会受到改变或影响,保持了可维护性。
//父类
public class Bird {

   public virtual void Fly() {
      Console.WriteLine("这只鸟在飞行");
   }

}

//子类 #1
public class sparrow : Bird {

   public override void Fly() {
     Console.WriteLine("麻雀在飞行");
   }

}

//子类 #2
public class Penguin : Bird {
   //企鹅当然不会飞... 所以这里传递了错误的信息..
   public override void Fly() {
     throw new NotImplementedException("企鹅不会飞!");
   }

}

你可以按全屏按钮进入全屏模式,再按退出全屏按钮退出全屏模式。

这违反了LSP,因为我们在任何需要Bird对象的地方使用penguin类……程序会出现意外情况,因为调用penguin类的Fly方法时会抛出异常……企鹅是不能飞的啊 😂

Q3: 猜猜我怎么做到LSP啊?(LSP)

    public abstract class Bird {
      public abstract void Display();
    }

    // 创建飞行行为的接口..
    public interface IFlyable {
      void Fly();
    } 

    // 现在我们不能正确地实现这个子类...
    public class sparrow : Bird, IFlyable {

      public override void Display () {
        Console.WriteLine("这是一只麻雀!!");
      }

     public void Fly() {
        Console.WriteLine("这只麻雀正在飞行");
      }
    }

    // 企鹅不具备飞行能力,因此不需要实现IFlyable接口。
    public class penguin : Bird {

      public override void Display () {
        Console.WriteLine("这是一只企鹅!");
      }

    }

全屏,退出全屏


4. 接口隔离原则 (ISP)
  • 定义:接口隔离原则指出,一个类不应被迫实现它不使用的接口。相反,较大的接口应该被拆分成更小、更具体的接口,使实现这些接口的类只需关心相关的那些方法。

  • 好处:避免类实现不必要的方法。使代码更模块化、更易懂。通过专注于特定功能来支持单一职责原则(SRP)。

违反了这个原则就是说:

    public interface IAnimal {
        void Eat();
        void Fly();
        void Swim();
    }

    public class Dog : IAnimal {
        public void Eat() {
            Console.WriteLine("狗正在吃东西。");
        }

        public void Fly() {
            throw new NotImplementedException(); // 狗不会飞
        }

        public void Swim() {
            Console.WriteLine("狗正在游泳。");
        }
    }

点击切换到全屏模式, 点击切换回正常模式

最后一个例子违反了接口隔离原则(ISP),因为它迫使Dog类实现与其行为无关或不必要的方法(如Fly)。这产生了几个问题。

我们试着按照这个原则来重构一下吧:

    public interface IEater {
        void Eat();
    }

    public interface ISwimmer {
        void Swim();
    }

    public class Dog : IEater, ISwimmer {
        public void Eat() {
            Console.WriteLine("狗在吃东西。");
        }

        public void Swim() {
            Console.WriteLine("狗在游泳。");
        }
    }

您可以点击这里进入全屏模式,点击这里退出全屏模式

通过将 IAnimal 接口拆分为更小且更具体的接口,比如 IEater、IFlyer 和 ISwimmer,每个类只需实现与其行为相关的接口。这样可以避免上述问题,同时符合 ISP(接口隔离原则)。这样可以使代码库更清晰、维护更简单且更具灵活性。


依赖倒置原则
  • 定义如下:高层模块不应该依赖底层模块。两者都应依赖于抽象概念(如接口或抽象类等)。

  • 优点:减少组件间的紧密耦合,使系统更加灵活,更容易进行重构或重置。

就这样吧!继续编程,继续保持出色。回头见!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消