S.O.L.I.D原则的重要性
> S.O.L.I.D principles
介绍
作为开发人员,我们一直在处理遗留代码库。 大多数传统代码库具有紧密耦合的类,冗余代码和较少的测试范围。 快速浏览代码时,这会使开发人员难以理解代码库的功能。
想象一下在类中遍历无数行代码以仅修复错误的痛苦。 开发人员最终可能会阅读更多的代码行,而不是编写更多的代码行。 此外,固定一个流程可能会导致另一个流程中断。 这使我想起以下著名的模因。
> Fix one bug & you have 10 more ready
由于在旧版软件中没有进行积极的开发,因此使开发人员和管理人员陷入了困境。 然后,团队考虑重新编写整个服务并弃用旧的服务。
为什么采用软件设计原则?
在当今不断发展的世界中,客户需求以前所未有的速度不断变化。 对于软件团队来说,适应新的需求并迅速发布变更变得至关重要。 为此,必须减少软件开发和测试时间。
同时,每隔一年就会引入新技术。 通常,通过替换现有技术来尝试更优化,更高效的技术。 因此,编写的代码必须灵活且松散地耦合以引入任何更改。
编写良好的代码很容易掌握。 新开发人员不必花更多的时间来阅读代码,而只需修改部分代码即可。 维护良好的软件可以提高开发人员和团队的生产力。 此外,高测试覆盖率增加了部署新变更的信心。
S.O.L.I.D是Michael Feathers创造的首字母缩写词,也是Robert Martin(鲍勃叔叔)发表的原理的子集。 我们将介绍这五个原则,并为每个原则进行说明。
S-单一责任原则
这是最简单的原理之一。 它指出"一个班级只有一个改变的理由"。 很多时候,您可能会发现一类执行的功能比预期的要多。
假设您正在编写银行软件的代码。 该功能是显示给定用户的声明。 该代码从数据库中获取数据,并以用户选择的格式显示数据。 您最终编写了以下代码。
> BankStatementManager
从上面的代码片段中可以看到," BankStatementMgr"类一次执行多项操作。 它正在从数据库中获取数据,解析结果,然后以用户指定的格式显示它。 您可以观察到以下缺陷:
· 没有责任隔离。 如果引入了新格式或添加了新的数据库列,则该类将需要更改
· 该类与数据库驱动程序紧密耦合。 数据库驱动程序或SQL查询中的任何更改都将导致对该类的修改
· 交易格式无法单独测试,因为BankStatementMgr不会公开交易格式
· 该代码不是模块化的,因为多个功能相互交织
以上缺点可以通过以下方法克服:
· 定义一个单独的格式化程序,其职责是格式化事务
· 添加一个数据库访问对象或DAO,它将封装数据库驱动程序并完成所有查询任务
· BankStatementMgr会将请求委托给DAO以获取数据,然后将响应传递给格式化程序以进行美化
· 通过这种方式,我们可以隔离地测试DAO和Formatter并实现松散耦合。 因此,它将通过分离职责来使代码模块化
以下是我们的修改代码:
> BankStatementManager
> StatementFormatter
> TransactionDAO
仍有很多改进的余地,我们将在下一部分中看到如何重构和使事情变得更好。
O — 开闭原理
该原则指出,代码应打开以进行扩展,并应关闭以进行修改。 如果需要添加新功能,则必须扩展该类。 此外,为使系统可扩展,应将其行为分开。
我们将通过一个例子来理解这一点。 假设您是接受不同方式付款的电子商务商人。 您已经集成了Paypal,Wepay,google Pay等不同的模式,并开发了付款处理器。 您提出以下代码。
> PaymentProcessor
> PaymentHandler
PaymentHandler处理付款请求。 PaymentProcessor确定模式并将其委托给正确的操作。 此代码违反了开闭原则,因为任何功能都需要在PaymentProcessor和PaymentHandler中进行修改。 此设计不可扩展,因为每种新的付款方式都会在switch语句中引入一个新的case块。
为了使代码可扩展,我们可以使PaymentHandler抽象,并定义一种处理Payment的方法。 为了处理新的付款方式,我们可以扩展此基类并覆盖其handlePayment方法。 下面是新代码。
> abstract PaymentHandler
> GooglePayHandler
> CardPaymentHandler
现在,我们将创建一个工厂类,该类将负责存储特定的处理程序并根据模式返回它。
> PaymentHandlerFactory
> PaymentProcessor
我们的新代码现在符合开闭原则。 要添加新的行为,我们只需要扩展抽象类PaymentHandler并在工厂中对其进行配置。 无需修改PaymentProcessor。
L-Liskov替代原理
乍一看,这个名字听起来很吓人。 该原理指出,同一超类的对象应该能够彼此替代而不破坏现有代码。
我们将以为电影开发剪贴簿为例。 抓取工具提供了一个界面,可以按电影名称或演员搜索电影。
> MovieSearch
> IMDB Search
> Rotten Tomatoes Search
> Client code using the MovieSearch interface
我们有两种不同的实现。 一个用于烂番茄,另一个用于IMDB。 它们都是可替换的,并且可以使用相同的接口进行访问。
如果未实现派生类中的方法,则会违反该原理。 以下是违反Liskov原理的示例。
> AllMoviesSearch
在这种情况下,我们无法将其他派生类(例如IMDB和烂番茄)替换为"所有电影"。 该方法未实现searchByMovieName方法,并且不会在客户端代码中导致一致的行为。
I —接口隔离
根据此原则,不应让客户实施不需要的方法。 如果您定义了客户端不使用的方法,则接口将变得过于庞大且受到污染。
如果某个接口因混合功能而变得太大,则可以将其隔离为多个较小的接口。 让我们看一下投资组合服务的示例,该服务允许客户订购股票,ETF,期权等
> Interface Portfolio
我们定义了一个接口Portfolio,该接口允许客户订购股票,ETF,以及两者的组合。
> ETFOrderService
> StockOrderService
如果我们决定在订购股票时增加价格作为参数怎么办? 它将需要更改orderStocks方法以接受价格作为参数。 此外,此更改必须由ETFOrderService合并,即使它不支持orderStocks方法。
为了克服这个问题,我们可以将接口分成两个部分-a)StockPortfolio b)ETFPortfolio
> StockPortfolio
> ETFPortfolio
使用新的接口,StockOrderService无需处理订购ETF。 这同样适用于ETFOrderService。
> ETFOrderService
> StockOrderService
界面隔离与"单一责任"和" Liskov替代原则"有一些相似之处。
在上面带有庞大接口的示例中,我们在StockOrderService中引发了Exception。 这违反了《里斯科夫换人原则》。 在这种情况下,派生类不会扩展功能。
如果在接口中定义了不相关的方法,则该类将有多个更改原因。 这违反了单一责任原则。
D —依赖倒置
根据依赖倒置,程序中的高级模块一定不能与低级模块紧密耦合。 两个模块都必须依赖抽象。 该原理提供了一种构建松耦合软件模块的机制。
让我们看看以下示例。 在此示例中,类OrderHistory从PostgreSQL数据存储中获取数据。
> OrderHistory
OrderHistory类必须知道PostgresDB依赖项的实现细节。 如果我们决定使用其他数据库驱动程序,则需要用新的依赖项替换所有PostgresDB实例。
此外,DB驱动程序更改的一项功能是什么? 它还需要在OrderHistory类中进行更改,以调用数据库驱动程序的方法。
可以通过声明接口DataStore除去此耦合。 此接口将公开使用者将调用的API。 我们可以有DataStore的多种实现-a)Postgres DataStore b)MySQL DataStore c)S3,等等
> DataStore
> PostgresDataStore
> OrderHistory
我们的消费者类别现在不必处理正在使用什么数据存储的底层细节。 高级模块OrderHistory依赖于接口DataStore来访问数据。 较低级别的DataStore实现的任何更改都不会对OrderHistory产生任何影响。
此外,由于模块松散耦合,因此可以独立测试它们。 使用依赖注入,可以轻松地将新的实现注入到高级模块中。
结论
以上五项原则构成了软件工程遵循的最佳实践的基石。 在日常工作中实践上述原理有助于提高软件的可读性,模块化,可扩展性和可测试性。
最终,它有助于构建易于维护的,易于维护的软件。 遵循上述做法有助于提高开发人员的生产率和工程团队的敏捷性。
参考资料
· Android中的扎实原则
· 简化基本原则
· 里斯科夫替代原则
· 通过应用SOLID原则成为更好的开发人员
· 五分钟的SOLID原理
· 封面照片
编码面试题 Skilled.dev
一个完整的平台,在该平台上,我可以教您找到下一份工作所需的一切,以及可以实现以下目标的技术:
(本文翻译自Viktors Telle的文章《SOLID Principles — Simplified with Illustrations》,参考:https://levelup.gitconnected.com/solid-principles-simplified-with-illustrations-fe5265f68ec6)