Tag: 开放封闭原则

开闭原则:正确扩展实体

在SOLID首字母缩略词“封闭式原理”中包含的所有设计原理中,可能是最难理解且最经常破解的。 毕竟,如果您想向实体中添加功能,为什么不直接将其添加到实体的现有实现中呢? 作为新手程序员,我肯定会感到这种感觉。 本文的目的是首先讨论“开放式封闭原则”背后的原理,然后研究三种正确扩展实体的技术。 什么是开放式封闭原则,为何如此重要? 让我们从Bertrand Meyer最初编写的定义开始: 软件实体(类,模块,功能等)应打开以进行扩展,但应关闭以进行修改 本质上,我们是在谈论以某种扩展方式向实体添加任何新属性或方法,而不是将它们直接放入实体中(然后进行修改)。 那是有道理的,但是为什么我们要在这个麻烦上走? 这是一些原因… 遏制错误的引入 假设我正在编写一个供第三方使用的软件包,并决定要向现有实体添加一些新功能。 从表面上看,这似乎很好。 但是,当我进行这些更改时,我并未考虑系统中其他实体与我刚刚修改的实体进行交互的所有不同方式。 我的修改会产生意想不到的影响,导致系统其他部分出现错误吗? 通常,我们希望逐步开发我们现有的代码,以使其尽可能高效且无错误。 在此过程中引入新的属性或方法可能会取消数月甚至数年的迭代改进。 单独关注 当我向现有实体介绍新功能时,很可能会打破SOLID原则中的不止一项。 如果新功能使我的实体超出其原始用途的范围,我还将打破“单一责任原则”。 为了代码可维护性,我应该保留原始实现,并使用其他方法对其进行扩展。 设计更好的实体 在采用开放式封闭原则的所有原因中,最重要的是它鼓励我们通过使用泛型和面向协议的编程(POP)原则来创建具有良好抽象平衡的实体。 试想一下,如果我知道我不能回到自己的实体来更改类型,那么我将非常谨慎地以能够提供最大灵活性的方式进行设计。 结果是我们最终可以编写更少的代码,并使我们编写的代码适用于更多情况。 简而言之,我们编码更好。 显然,这里可能会有更多的讨论,但是为了继续学习一些有用的技术,让我们考虑最后一个大问题…… 我应该严格遵循开放式原则吗? 遵守开放式封闭原则是一个主观选择,开发人员通常在何时扩展实体上有不同的看法。 毕竟,您的实体可能被认为是“完成”或“进行中的工作”,最终您作为开发者是做出这种区分的仲裁者。 以我的经验,如果您决定实体需要您想要实现的功能来履行其单一职责,那么请继续添加它们(无罪感!)。 否则,这里有一些有关如何正确扩展它们的想法。 扩展技术 在下一部分中,我们将介绍三种扩展技术并对其进行批判。 事不宜迟,让我们熟悉在以下示例中将要使用的惊人实体: 是的,这真是好人。 技术#1:协议扩展(OK) 假设我希望我的“ Thingy”能够摆动(不要问我为什么……)。 我的第一个直觉可能是这样做: 显然,这违反了我们的开放式封闭原则。 因此,一种方法可能是创建一个声明该方法的协议,然后将其实现放入我们的struct的扩展中: 很好。 请注意,方法的声明发生在协议中(第9行),其实现发生在实体的扩展中(第15行)。 我们可能考虑的一种变体是将方法的实现放入协议扩展中。 这将使我们能够在符合协议的任何实体上使用相同的方法实现。 看起来像这样: 这里要注意的最大区别是,我们的摆动方法的声明和实现现在都在协议的扩展中(第9-15行)。 当实体符合协议时(第17行),现在无需实现该方法。 因此,通过第一组示例,我们从技术上扩展了我们的实体,而没有对其进行修改,但是请注意! 首先,这种技术仅适用于方法,不适用于属性,因为我们无法在扩展中存储任何内容。 其次,也许还有一个更大(但不太明显)的缺点是,我们不再能够像添加摆动方法之前那样使用“ Thingy”的原始实现。 技术#2:类继承(OK) […]

OCP:开放/封闭原则

开放/封闭原则促进了协议的使用,使您能够在不更改现有代码的情况下适应应用程序的功能。 早先的“打开/关闭”原理由Bertrand Meyer在1988年定义: “软件实体(类,模块,功能等)应打开以进行扩展,但应关闭以进行修改。” 这样的实体可以允许在不更改其源代码的情况下更改其行为。 不幸的是,Bertrand Mayer建议使用继承来实现此目标: “类是封闭的,因为它可以被编译,存储在库中,作为基线并由客户端类使用。 但是它也是开放的,因为任何新类都可以将其用作父类,从而增加新功能。 定义后代类时,无需更改原始类或打扰其客户。” 如果子类依赖于其父类的实现细节,则继承会引入紧密耦合。 Robert C. Martin和其他人将开放/封闭原则重新定义为多态开放/封闭原则。 它使用协议而不是超类来允许不同的实现,您可以在不更改使用它们的代码的情况下轻松替换它们。 协议已关闭,无法进行修改,您可以提供新的实现方式来扩展软件的功能。 这种方法的主要好处是协议引入了额外的抽象级别,从而实现了松散耦合。 协议的实现彼此独立,不需要共享任何代码。 如果您认为协议的两个实现共享一些代码是有好处的,则可以在协议扩展中提供实现。 聪明的应用程序设计和代码编写部分应注意在应用程序的开发和维护阶段进行的频繁更改。 开放关闭原则指出,代码的设计和编写应以添加新功能的方式完成,而对现有代码的更改最少。 设计应以允许将新功能作为新类添加的方式进行,并尽可能使现有代码保持不变。 在许多方面,该原理都是面向对象设计的核心。 遵循这一原则的是可产生最大收益的面向对象技术,如可重用性和可维护性。 罗伯特·马丁(Robert Martin)引用的敏捷原则,模式和实践: 符合OCP的模块具有两个主要属性。 它们对扩展开放,这意味着可以扩展模块的行为。 随着应用程序需求的变化,我们可以使用满足这些变化的新行为来扩展模块。 换句话说,我们能够更改模块的功能。 它们已关闭以进行修改。 扩展模块的行为不会导致模块的源代码或二进制代码更改。 模块的二进制可执行版本(无论是在可链接库,DLL还是.EXE文件中)均保持不变。 TDD不仅与测试有关,更重要的是与设计有关。 有许多设计模式可以帮助我们扩展代码而不进行更改。 例如,装饰器模式可帮助我们遵循“打开关闭”原理。 同样,工厂方法,策略模式或观察者模式也可用于设计易于更改的应用程序,而只需对现有代码进行最少的更改即可。 在SRP中,您需要对分解以及代码中绘制封装边界的位置进行判断。 在OCP中,您将判断要在模块中进行哪些抽象,然后让模块的使用者进行具体设计,以及要自己提供什么具体功能。 关于这个原理,有一个有趣的类比,它指出“ 穿大衣时不需要开胸手术 。”这意味着,作为程序员,我们不应该冒险更改核心功能来添加简单功能并可能损害数据完整性。 本文在计算面积方面很好地说明了OCP的示例: https://academy.realm.io/posts/donn-felker-solid-part-2/ 让我们考虑另一个创建表单库的示例。 库为label,textField和textView提供了几个字段。 一段时间后,库开始出货,并且新的要求也开始为Switch UI提供支持。 如果在此枚举中添加新案例,并且客户端在switch语句中使用RowType ,则现有代码将中断,用户将不再使用库。 如果我们遵循开放/封闭原则,那么我们的系统将始终是可扩展的。 这些是Foundation和UIKit框架使用的“开放/封闭原则”的几个示例: URLSessionConfiguration default, ephemeral […]

Swift中的SOLID原理

SOLID是罗伯特·C·马丁(Robert C. Martin)在软件编程中使用的助记词缩写,它表示面向对象编程的5条原则。 单一责任原则 O笔已关闭 利斯科夫的替补 接口隔离 倾向倒置 这些原则解决了糟糕的架构问题,例如 美味佳肴 -某个地方的更改会破坏意外的部分,如果没有良好的测试覆盖范围,则很难跟踪。 固定性 -组件很难在另一个项目中重用或在同一项目的多个位置中使用,因为它具有许多依赖性。 易怒 -进行大量更改,因为它会影响多个部分。 这些SOLID原则不是严格的规则,而只是提高体系结构质量的准则。 让我们一一探究原理