软件架构基础之SOLID原则

admin 2022年6月22日12:58:46评论23 views字数 2843阅读9分28秒阅读模式

 


理论基础

SOLID 原则是经过长时间考验,它主要用于创建高质量软件的准则。

SOLID是从 Robert C. Martin 在 2000 年代初期的著作中提炼出来的一套原则。它被提议作为一种专门考虑面向对象(OO)编程质量的方法。

作为一个整体,SOLID 原则为代码应该如何拆分、哪些部分应该是内部的或公开的以及代码应该如何使用其他代码提出了争论。

下面将对SOLID的发展历程及五大原则进行解析。

 

SOLID发展

在 2000 年代初期,Java 和 C++ 是主流编码语言。当然,在哪个时候的开发过程中,Java 是我们选择的语言,我们的大部分练习和课程都使用它。Java 的流行催生了一个由书籍、会议、课程和其他材料组成的家庭手工业,以促使人们从编写代码到编写好代码。

对软件架构基础发展,下面是值得我们关注的:

  • Python、Ruby,尤其是 JavaScript 等动态类型语言已经变得与 Java 一样流行——在某些行业和类型的公司中超过了它。


  • 非面向对象的范例,尤其是函数式编程 (FP),在这些新语言中也更为常见。甚至 Java 本身也引入了 lambdas!元编程(添加和更改对象的方法和特性)等技术也很受欢迎。还有一些“更软”的 OO 风格,例如 Go,它具有静态类型但没有继承。所有这些都意味着类和继承在现代软件中的重要性不如过去。


  • 开源软件激增。以前,最常见的做法是编写供客户使用的闭源编译软件,而如今,开发的产品是基于开源的代码进行二次开发。正因为如此,在编码开发库文件时曾经势在必行的那种逻辑和数据隐藏不再那么重要了。


  • 微服务和软件即服务如雨后春笋般涌现。与其将应用程序部署为将其所有依赖项链接在一起的大型可执行文件,不如部署与其他服务(自己的或由第三方提供支持的服务)对话的小型服务更为常见。


 

SOLID的单一责任原则


 

原始定义:“一个类更改的原因不应超过一个。”

如果在编写的类有很多关注点或“改变的原因”,那么每当这些关注点中的任何一个必须改变时,你都需要更改相同的代码。这增加了对一个功能的更改会意外破坏另一个功能的可能性。

例如,下面是一个franken类,它永远不会进入生产:

软件架构基础之SOLID原则

新定义:“每个模块都应该做一件事并做好。”

这一原则与高内聚主题密切相关。本质上,代码不应将多个角色或用途混合在一起。

这是用 JavaScript 的同一示例的 FP 版本:

软件架构基础之SOLID原则


这也适用于微服务设计;如果你有一个服务可以处理所有这三个功能,那么它会尝试做太多事情。 

SOLID的开闭原则

原始定义:“软件实体应该对扩展开放,但对修改关闭。”

这是 Java 等语言设计的一部分——你可以创建类并扩展它们(通过创建子类),但不能修改原始类。 

“开放扩展”的一个原因是限制对类作者的依赖。如果需要更改类,你需要不断要求原始作者为你更改,或者你需要亲自深入修改。更重要的是,该课程将开始纳入许多不同的关注点,这打破了单一责任原则。

关闭类进行修改的原因,我们可能不相信任何和所有下游消费者理解我们用来使我们的功能工作的所有“私有”代码,并且我们希望保护它不被不熟练的人使用。

软件架构基础之SOLID原则


新定义:“应该能够在不重写模块的情况下使用和添加模块。”

这在 OO-land 中是免费的。在 FP 世界中,你的代码必须定义明确的“挂钩点”以允许修改。

下面代码,它不仅允许前后挂钩,甚至可以通过将函数传递给函数来覆盖基本行为:

软件架构基础之SOLID原则


 

SOLID的里氏替换原则


 

原始定义:“如果 S 是T 的子类型,则 T 类型的对象可以被 S 类型的对象替换,而不会改变程序的任何所需属性。”

这是面向对象语言的基本属性。这意味着你应该能够使用任何子类代替其父类。这可以让你对你的合约充满信心——你可以安全地依赖任何“是”类型的对象T来继续表现得像T一样工作。

下面是一个实例:

软件架构基础之SOLID原则


新定义: 如果声明这些事物的行为方式相同,您应该能够用一种事物替换另一种事物。

在动态语言中,重要的是,如果你的程序“承诺”做某事(例如实现接口或函数),你需要信守承诺,不要让客户感到惊讶。

许多动态语言都使用duck类型来实现这一点。从本质上讲,您的函数正式或非正式地声明,它期望其输入以特定的方式运行,并根据该假设进行操作。

下面是 Ruby 语言的例字:

软件架构基础之SOLID原则


在这种情况下,函数并不关心类型input是什么——只关心它的to_s函数的行为方式与所有to_s函数的行为方式相同,即将输入转换为字符串。许多动态语言没有办法强制这种行为,因此这更像是一个学科问题,而不是一种形式化的技术。

下面是 TypeScript 的 FP 示例。在这种情况下,高阶函数接受一个过滤器函数,该函数需要一个数字输入并返回一个布尔值:

软件架构基础之SOLID原则


 

SOLID的接口隔离原则

原始定义:“许多特定于客户端的接口都比一个通用接口好。”

在 OO 中,你可以将其视为为您的类提供“视图”。与其将完整的实现提供给所有客户端,不如在它们之上创建仅使用与该客户端相关的方法的接口,并要求你的客户端使用这些接口。 

与单一职责原则一样,这减少了系统之间的耦合,并确保客户端不需要了解或依赖它无意使用的功能。

这是一个通过 SRP 测试的示例:

软件架构基础之SOLID原则


这段代码通常只有一个“改变的理由”——它都与打印请求有关,它们都是同一个域的一部分,并且所有三种方法都可能改变相同的状态。但是,创建请求的同一客户端不太可能是处理请求的客户端。将这些接口分开会更有意义:

软件架构基础之SOLID原则


新定义:“不要向客户展示超出他们需要看到的内容”。


只记录客户需要知道的内容。这可能意味着使用文档生成器只输出“公共”函数或路由,而不输出“私有”函数或路由。


在微服务世界中,可以使用文档或真正的分离来增强清晰度。例如,你的外部客户可能只能以用户身份登录,但内部服务可能需要获取用户列表或其他属性。可以通过创建一个单独的“仅限外部”用户服务来调用主服务,也可以只为外部用户输出特定的文档来隐藏内部路由。

 

SOLID的依赖倒置原则

原始定义:“取决于抽象,而不是具体。”

在面向对象中,这意味着客户端应该尽可能依赖于接口,而不是具体的类。这确保了代码依赖于尽可能小的表面积事实上,它根本不依赖于代码,而只是定义代码应该如何运行的契约。与其他原则一样,这降低了一处破损导致其他地方意外破损的风险。

下面是一个简单的示例:

软件架构基础之SOLID原则

如果正在编写需要记录器的代码,不想将自己限制在写入文件,因为你不在乎。因此只需调用该write方法并让具体类对其进行排序。

新定义: “取决于抽象,而不是具体。”

在这种情况下,我将保持原样!尽可能保持抽象的想法仍然很重要,即使现代代码中的抽象机制不如严格的 OO 世界中那么强大。

实际上,这几乎与上面讨论的 Liskov 替换原则相同。主要区别在于,这里没有默认实现。因此,该部分中涉及鸭子类型和钩子函数的讨论同样适用于依赖倒置。

还可以将抽象应用于微服务世界。例如,可以用消息总线或队列平台(如 Kafka 或 RabbitMQ)代替服务之间的直接通信。这样做允许服务将消息发送到单个通用位置,而无需关心哪个特定服务将接收这些消息并执行其任务。 

软件架构基础之SOLID原则结束软件架构基础之SOLID原则

 

原文始发于微信公众号(安全架构):软件架构基础之SOLID原则

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月22日12:58:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   软件架构基础之SOLID原则http://cn-sec.com/archives/1134157.html

发表评论

匿名网友 填写信息