浅谈 SOLID 原则的具体使用

article/2025/9/23 0:45:52
  • 单一职责原则(SRP)
  • 开放封闭原则(OCP)
  • 里氏替换原则(LSP)
  • 接口隔离原则(ISP)
  • 依赖倒置原则(DIP)
  • 小结

SOLID 是面向对象设计5大重要原则的首字母缩写,当我们设计类和模块时,遵守 SOLID 原则可以让软件更加健壮和稳定。那么,什么是 SOLID 原则呢?本篇文章我将谈谈 SOLID 原则在软件开发中的具体使用。

回到顶部

单一职责原则(SRP)

单一职责原则(SRP)表明一个类有且只有一个职责。一个类就像容器一样,它能添加任意数量的属性、方法等。然而,如果你试图让一个类实现太多,很快这个类就会变得笨重。任意小的改变都将导致这个单一类的变化。当你改了这个类,你将需要重新测试一遍。如果你遵守 SRP,你的类将变得简洁和灵活。每一个类将负责单一的问题、任务或者它关注的点,这种方式你只需要改变相应的类,只有这个类需要再次测试。SRP 核心是把整个问题分为小部分,并且每个小部分都将通过一个单独的类负责。

假设你在构建一个应用程序,其中有个模块是根据条件搜索顾客并以Excel形式导出。随着业务的发展,搜索条件会不断增加,导出数据的分类也会不断增加。如果此时将搜索与数据导出功能放在同一个类中,势必会变的笨重起来,即使是微小的改动,也可能影响其他功能。所以根据单一职责原则,一个类只有一个职责,故创建两个单独的类,分别处理搜索以及导出数据。

回到顶部

开放封闭原则(OCP)

开放封闭原则(OCP)指出,一个类应该对扩展开放,对修改关闭。这意味一旦你创建了一个类并且应用程序的其他部分开始使用它,你不应该修改它。为什么呢?因为如果你改变它,很可能你的改变会引发系统的崩溃。如果你需要一些额外功能,你应该扩展这个类而不是修改它。使用这种方式,现有系统不会看到任何新变化的影响。同时,你只需要测试新创建的类。

假设你现在正在开发一个 Web 应用程序,包括一个在线纳税计算器。用户可以访问Web 页面,指定他们的收入和费用的细节,并使用一些数学公式来计算应纳税额。考虑到这一点,你创建了如下类:

public class TaxCalculator
{public decimal Calculate(decimal income, decimal deduction, string country){decimal taxAmount = 0;decimal taxableIncome = income - deduction;switch (country){case "India"://Todo calculationbreak;case "USA"://Todo calculation break;case "UK"://Todocalculationbreak;}return taxAmount;}
}

这个方法非常简单,通过指定收入和支出,可以动态切换不同的国家计算不同的纳税额。但这里隐含了一个问题,它只考虑了3个国家。当这个 Web 应用变得越来越流行时,越来越多的国家将被加进来,你不得不去修改 Calculate 方法。这违反了开放封闭原则,有可能你的修改会导致系统其他模块的崩溃。

让我们对这个功能进行重构,以符合对扩展是开放,对修改是封闭的。

根据类图,可以看到通过继承实现横向的扩展,并且不会引发对其他不相关类的修改。这时 TaxCalculator 类中的 Calculate 方法会异常简单:

public decimal Calculate(CountryTaxCalculator obj)
{decimal taxAmount = 0;taxAmount = obj.CalculateTaxAmount();return taxAmount;
}
回到顶部

里氏替换原则(LSP)

里氏替换原则指出,派生的子类应该是可替换基类的,也就是说任何基类可以出现的地方,子类一定可以出现。值得注意的是,当你通过继承实现多态行为时,如果派生类没有遵守LSP,可能会让系统引发异常。所以请谨慎使用继承,只有确定是“is-a”的关系时才使用继承。

假设你在开发一个大的门户网站,并提供很多定制的功能给终端用户,根据用户的级别,系统提供了不同级别的设定。考虑到这个需求,设计如下类图:

可以看到,ISettings 接口有 GlobalSettings、SectionSettings 以及 UserSettings 三个不同的实现。GlobalSettings 设置会影响整个应用程序,例如标题、主题等。SectionSettings 适用于门户的各个部分,如新闻、天气、体育等设置。UserSettings 为特定登录用户设置,如电子邮件和通知偏好。

这样的设计没问题,但如果有另一个需求,系统需要支持游客访问,唯一区别是游客不支持系统的设定,为了满足这个需求,你可能会如下设计:

public class GuestSettings : ISettings
{public void GetSettings(){//get settings from database//include guest name、ip address...}public void SetSettings(){//guests are not allowed set settingsthrow new NotImplementedException();}
}

这样没问题吗?准确来说,系统存在隐患。当单独使用 GuestSettings 时,因为我们了解游客不能设置,所以我们潜意识并不会主动调用 SetSettings 方法。但是由于多态,ISettings 接口的实现可以被替换为 GuestSettings 对象,当调用SetSettings 方法时,可能会引发系统异常。

重构这个功能,拆分为两个不同的接口:IReadableSettings 和 IWritableSettings。子类根据需求实现所需的接口。

回到顶部

接口隔离原则(ISP)

接口隔离原则(ISP)表明类不应该被迫依赖他们不使用的方法,也就是说一个接口应该拥有尽可能少的行为,它是精简的,也是单一的。

假设你正在开发一个电子商务的网站,需要有一个购物车和关联订单处理机制。你设计一个接口 IOrderProcessor,它用包含一个验证信用卡是否有效的方法(ValidateCardInfo)以及收件人地址是否有效的方法(ValidateShippingAddress)。与此同时,创建一个OnlineOrderProcessor 的类表示在线支付。

这非常好,你的网站也能正常工作。现在让我们来考虑另一种情形,假设在线信用卡支付不再有效,公司决定接受货到付款支付。
乍一看,这个解决方案听起来很简单,你可以创建一个CashOnDeliveryProcessor 并实现 IOrderProcessor 接口。货到付款的购买方式不会涉及任何信贷卡验证,所以,CashOnDeliveryOrderProcessor 类内部的 ValidateCardInfo 方法抛出 NotImplementedException。

这样的设计在未来可能会出现的潜在问题。假设由于某种原因在线信用用卡付款需要额外的验证步骤。自然,IOrderProcessor 将被修改,它将包括那些额外的方法,于此同时 OnlineOrderProcessor 将实现这些额外的方法。然而,CashOnDeliveryOrderProcessor 尽管不需要任何的附加功能,但你必须实现这些附加的功能。显然,这违反了接口隔离原则。

你需要将这个功能重构:

新的设计分成两个接口。IOrderProcessor 接口只包含两个方法:ValidateShippingAddress 和 ProcessOrder,而 ValidateCardInfo 抽象到到一个单独的接口:IOnlineOrderProcessor。现在,在线信用卡支付的任何改变只局限于IOnlineOrderProcessor 和它的子类实现,而 CashOnDeliveryOrderProcessor 是不会被影响。因此,新设计符合接口隔离原则。

回到顶部

依赖倒置原则(DIP)

依赖倒置原则(DIP)表明高层模块不应该依赖低层模块,相反,他们应该依赖抽象类或者接口。这意味着你不应该在高层模块中使用具体的低层模块。因为这样的话,高层模块变得紧耦合低层模块。如果明天,你改变了低层模块,那么高层模块也会被修改。根据DIP原则,高层模块应该依赖抽象(以抽象类或者接口的形式),低层模块也是如此。通过面向接口(抽象类)编程,紧耦合被移除。

那么什么是高层模块,什么是低层模块呢?通常情况下,我们会在一个类(高层模块)的内部实例化它依赖的对象(低层模块),这样势必造成两者的紧耦合,任何依赖对象的改变都将引起类的改变。

依赖倒置原则表明高层模块、低层模块都依赖于抽象,举个例子,你现在正在开发一个通知系统,当用户改变密码时,邮件通知用户。

public class UserManager
{public void ChangePassword(string username,string oldpwd,string newpwd){EmailNotifier notifier = new EmailNotifier();//add some logic and change password //Notify the usernotifier.Notify("Password was changed on "+DateTime.Now);}
}

这样的实现在功能上没有问题,但试想一下,新的需求希望通过SNS形式通知用户,那么我们只能手动将EmaiNorifier 替换为 SNSNotifier。在这儿,UserManager 就是高层模块,而EmailNotifier 就是低层模块,他们彼此耦合。我们希望解耦,依赖于抽象 INotifier,也就是面向接口的编程。

回到顶部

小结

本篇博客为大家介绍了面向对象设计的 SOLID 原则,并以具体的案例辅助讲解。你可以看到,继承和多态在SOLID 原则中扮演了非常重要的角色。我们的应用程序不能过度设计,当然也不能随意设计。了解基本的 SOLID 原则能让你的应用程序变得健壮。你可以在Github 上查看具体的示例代码:https://github.com/MEyes/SOLID.Principles


http://chatgpt.dhexx.cn/article/skZZdSXm.shtml

相关文章

设计模式之SOLID原则再回首

本科阶段学过设计模式,那时对设计模式的五大原则——SOLID原则的概念与理解还是比较模糊,此时过去了2年时间,在学习《高级软件工程》课程中老师又提到了设计模式,课程中还详细讨论了五大原则的过程,这次SOLID原则再回首作者提出了一些更通俗的理解吧~ 一. 什么是设计模式&…

程序设计原则之SOLID原则

设计模式中的SOLID原则,分别是单一原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。前辈们总结出来的,遵循五大原则可以使程序解决紧耦合,更加健壮。 SOLID原则是由5个设计原则组成,SOLID对应每个原则英文字母的开头…

SOLID原则

SOLID原则是一组设计原则,它们旨在帮助开发人员创建易于维护和可扩展的软件系统,这些原则的缩写代表以下5个原则: 1. 单一职责原则(SRP):一个类应该只有一个职责。 2. 开闭原则(OCP)…

【KAFKA】kafka可视化工具kafkaTool 免费下载

【资源是免费的,官网可下载,可是官网下载的网络实在是太慢了有时候还会断线,我也是花了很长时间才下载下来的,提供给大家一个方便】 符合kafka version 0.11 mac 版:链接:https://pan.baidu.com/s/1q6qKrEbaDGukvqH…

windows 安装kafka流程

1、安装jdk 安装地址:www.oracle.com/java/technologies/downloads 下载好后进行安装,基本上一路点击下一步,不要忘记了把安装目录更换一下! 安装好后需要配置环境变量 找到 "计算机-属性-高级系统设置-高级-环境变量“ 1&…

Window下安装Kafka

目录 一、下载安装 二、配置 三、启动 一、下载安装 注意:Kafka安装文件中包含zookeeper 首先打开Kafka的网站:https://kafka.apache.org/ 点击 Download Kafka,选择适合的版本进行下载。 这里后缀 .tgz 格式文件兼容Windows系统&#x…

kafka的安装和使用(详细版)

原创地址: https://www.cnblogs.com/lilixin/p/5775877.html Kafka安装与使用 下载地址:https://www.apache.org/dyn/closer.cgi?path/kafka/0.8.1.1/kafka_2.10-0.8.1.1.tgz 安装以及启动kafka 步骤1:安装kafka $ tar -xzf kafka_2.10-…

kafka-manager 的下载及安装

kafka-manager 的下载及安装 kafka-manager的功能 为了简化开发者和服务工程师维护Kafka集群的工作,yahoo构建了一个叫做Kafka管理器的基于Web工具,叫做 Kafka Manager。 这个管理工具可以很容易地发现分布在集群中的哪些topic分布不均匀,或…

怎样安装Kafka?

1、Kafka是Java开发的应用程序,可以运行在Windows、 MacOS和 Linux等多 种操作系统上。最常见的是将Kafka安装在Linux系统上。 2、在安装Kafka之前,需要先安装Java环境,虽然运行 Zookeeper 和 Kafka 只需要 Java运行时版本,但也可…

Kafka锦集(一):Kafka的介绍 | 下载和安装 | kafka服务无法关闭 | bin/kafka-server-stop.sh无效 | 总结的很详细

前言 从本篇开始,带你一起领略Kafka的世界,下面重点介绍它的下载、安装,带你避坑。 提示:如果你只是想解决“bin/kafka-server-stop.sh无效”的问题,直接下滑到文章尾部的4.2章节进行查看! 一、Kafka介绍…

docker 下载kafka

Kafka采用的是订阅-发布的模式,消费者主动的去kafka集群拉取消息,与producer相同的是,消费者在拉取消息的时候也是找leader去拉取。 kafka存在的意义:去耦合、异步、中间件的消息系统 首先安装zookeeper docker search zookeepe…

windows下安装kafka

一.下载 kafka官网下载地址:http://kafka.apache.org/downloads.html,下载二进制的. 二.安装 1.安装zookeeper windows环境下安装zookeeper(单机版) 安装并启动后的界面: 2.安装kafka 我下载的kafka_2.13-2.8.0.tgz,并解压到D:\Tools\kafka_2.13-2.8.0目录下 编辑文件Kaf…

kafka tool下载安装和使用

一、下载安装 下载连接:https://www.kafkatool.com/download.html kafka tool官网介绍 Kafka工具是用于管理和使用Apache Kafka集群的GUI应用程序。 它提供了一种直观的UI,可让用户快速查看Kafka集群中的对象以及集群主题中存储的消息。 它包含面向开发…

[kafka] windows下安装kafka(含安装包)

[kafka] windows下安装kafka(含安装包) 目录 前言 一、下载kafka安装包 1)下载安装包 2)解压安装包 二、运行zookeeper 1.运行zookeeper(因为kafka必须要和zookeeper一起运行) 三、运行kafka 四、使用fafka…

1.Kafka下载安装

原文:kafka下载安装 一、安装jdk 参见:Linux环境下安装jdk1.8(安装包版) 二、安装kafka kafka安装包 链接:https://pan.baidu.com/s/1hy8XONH75fU-Djb_GBC-GA?pwdnmrs 提取码:nmrs1.解压kafka &…

Kafka在Linux下载安装及部署

前期准备工作: kafka的安装及使用需要用到ZooKeeper,所以需要提前安装搭建好ZooKeeper ZooKeeper在Linux下载安装及部署: Zookeeper在Linux下载安装及部署_学弟不想努力了-CSDN博客_zookeeper下载安装linuxhttps://blog.csdn.net/Eternal_Bl…

Kafka的常用命令(包括:下载安装、后台启动)

一、环境准备 首先JDK要在1.8及以上;然后安装对应版本的zookeeper。 本文以kafka2.7.2为例,关于如何找到kafka对应的zookeeper版本,参考我的这篇文章:如何确定kafka与zookeeper版本的对应关系。 1、安装Zookeeper3.5.9 Zookeep…

Kafka在windows下下载、启动、测试详细教程

目录 下载地址 启动 启动zookeeper 启动kafka 队列操作 创建消息队列名 删除消息队列名 查看所有的队列 测试 生产测试 消费测试 下载地址 Apache KafkaApache Kafka: A Distributed Streaming Platform.https://kafka.apache.org/downloadswindows下kafka3.0版本的…

kafka 下载安装

文章目录 第一章 kafka概述一、定义二、消息队列1、传统消息队列2、消息队列的两种模式(1)点对点模式(2)发布/订阅模式 三、kafka基础架构 第二章 Kafka安装一、安装部署1、集群规划 二、集群部署1、下载地址(1&#x…

kafka下载与安装教程

Kafka下载安装教程 1.定义2.特性3.使用场景4.1.下载jar包4.2.解压到指定的文件夹4.3.修改配置文件4.4.启动kafka内置的zookeeper4.5.启动kafka服务4.6.创建一个名为 test1 的tiopic的测试主体 kafka4.7.创建生产消息的生产者4.8.创建消息消费者接收消息 1.定义 Kafka传统定义&…