SOLID体系结构开发原理的描述和解释存在问题(Robert Martin著作)。 许多资料都给出了它们的定义,甚至给出了使用示例。 研究它们并尝试使用自己时,我不断地陷入困境,以为对它们的应用魔术性没有足够的解释。 试图看清内部齿轮,理解-对我而言意味着要记住-将其布置在其“术语架”中。 好吧,如果这对其他人有用。

我们继续“杂耍架子”以上设计方法。
单一责任原则(SRP)唯一责任原则
一段代码仅应在实现一个目标的过程中进行更改。 如果一段代码实现了两项任务并针对不同用途进行了更改,则应出于各种目的在实例中复制此部分。 这是非常重要的,因为它需要偏离公认的消除重复的原则。
该原理的目的是消除由于以下不变量而导致的隐式错误,这些不变量存在于代码段,过程,类,组件的开发中(以下术语[component]用于组合这些概念):
- [1]正确编写的[component]必须使用,并且经常使用几次,
- [2]在每个使用地点,[组件]都应保持一致的行为,从而导致可重复的结果,
- [3]在多个位置使用[component]时,结果应满足每个使用位置的要求,
- 如果其中一个使用场所需要[组件]的更改,而另一个使用场所需要[组件]的先前行为,则有必要创建[组件]的副本,然后对其进行修改(或使用提供不同行为的其他参数来概括[组件]),
如果[组件]的使用位置对于程序员解决的当前任务不重要,那么他很容易忘记检查与对此[组件]所做的更改的这些使用位置的兼容性。
因此,所有使用地点都应位于单一职责的[单一职责]区域中,也就是说,对于程序员解决的任何问题,应立即对其进行更改和考虑)。
该原理既适用于一段代码,也适用于在多个地方使用的组件,库,程序和程序集。
许多资料提供了一个示例,该类仅将一个“功能”作为SRP的理想选择,而将“神圣对象”的类别结合了应用程序的所有功能,作为一种反模式。 IMHO类仅具有一个“功能”是对代码体系结构进行过早优化的要求,它提示从头开始编写许多类(代码实体),而忘记了缺少多个使用位置可以使程序员快速评估位于本地的少量(在一个类中)交互代码,而不是分析负责其“功能”的不同代码实体的外部关系。 小型应用程序的“神圣对象”也不是罪魁祸首,它使您可以开始开发:选择所有必需的实体,并与标准库的外部对象和外部模块分开并排编写(创建活细胞并用膜将其分开)。 在项目的成长和发展过程中,有许多方法可以帮助您遵循SRP,其中之一是将类别划分为各个类别,并最大程度地减少每个类别负责的“职能”数量(细胞分裂及其在体内的专业化)。
在这里,我想写出一套维护SRP的技术,但是这项工作尚未完成(我希望“动手”)。 在明显的地方,您可以找到这些技巧:
- 设计模式;
- 使用不同的专用组件分支,而不是创建一个满足所有应用程序方法的组件(在GitHub上为fork)。
开闭原则(OCP)开/闭原则
计划代码的开发是最佳方案,以便程序员执行新任务时,有必要添加新代码,而旧代码不需要更改。 代码必须打开(打开)才能添加,关闭(关闭)才能更改。
该原则的目的是最大程度地减少人工成本,并消除由于以下开发中的不变量而导致的隐式错误:
- 先前描述的[1],[2],[3],
- 为了执行新任务,程序员可以添加新的[components]或更改旧[components]的行为,
- [component]的添加需要在新的使用位置进行验证,并花费程序员时间
- 由于新任务导致[组件]的行为发生变化,因此需要在新使用场所和所有旧使用场所进行验证,这也将导致程序员的时间消耗,对于已发布的[组件],则是使用该[组件]的所有程序员的工作。
建议选择一个选项来执行新任务,同时尽量减少程序员所花费的时间。
在软件开发实践中,添加成本通常比更改成本要少得多,这显然使使用[Open-Closed]原理的好处明显。 同时,有许多技术可将程序体系结构维持在新任务的实现归结为仅添加[components]的状态下。 与体系结构进行这种工作还需要程序员的时间,但是在大型项目中进行实践所显示的内容远远少于使用更改旧过程的方法。 而且,当然,这种发展描述是理想化的。 仅通过添加或更改就几乎无法实现任务。 在现实生活中,混合使用了这些方法,但是OCP强调使用添加方法的好处。
在这里,我想写出一套维护OCP的技术。 在明显的地方,您可以找到这些技巧:
- 设计模式;
- dll库及其用于分发,更新和开发功能的选项;
- 开发COM库和其中的对象;
- 开发编程语言并支持以前编写的代码;
- 发展国家立法制度。
Liskov替代原理(LSP)芭芭拉Liskov替代原理
该原则将基本接口[base]扩展的使用限制为实现,指出基本接口的每个实现都应具有作为基本接口的行为。 同时,基本界面在其使用位置固定了预期的行为。 在实现行为中,与预期行为存在差异(由基本接口修复)的情况将导致违反不变式的可能性[2]。
该原理基于并完善了基于抽象的设计技术。 在这种方法中,引入了一种抽象-固定了许多情况的一些基本属性和行为特征。 例如,针对以下情况的[component-procedure]“移至上一个位置”:“文本中的游标”,“书架上的书”,“数组中的元素”,“舞蹈中的脚”等。并分配给此[component](通常是根据日常经验而没有形式化)一些先决条件和行为,例如:“可移动对象的存在”,“重复多次”,“元素顺序的存在”,“元素固定位置的存在”。 LSP要求在为[component]添加新的使用情况时,必须满足基础的所有先决条件和限制。 不能用这种抽象来描述“糖罐中的谷物”的情况,尽管谷物当然有位置,有谷物曾经存在过的位置,并且可以在其中移动-元素没有固定的位置。
该原则的目的是消除由于以下开发中的不变而引入的隐式错误:
- 先前描述的[1],[2],[3],
- 基本的[程序]描述了一种在很多情况下有用的行为,设置了其适用性所需的约束,
为实施该基地而制定的[程序]必须满足其所有限制,包括难以追踪的隐含限制(非正式提供)。
通常,给出一个带有矩形([base])和正方形(实现)的示例来描述此原理。 情景class CSquare : public CRectangle
。 在[base]中,介绍了处理宽度和高度的操作(设置(获取)宽度,设置(获取)高度)。 在CSquare的实现中,这些Set操作被迫更改对象的两个大小。 我始终缺乏这样的解释,即在“基础”中“非正式地”设置了以下限制:“独立使用Width,Height的能力”。 在CSquare实现中,它被违反,并且在使用该独立性的地方使用简单的操作序列: r.SetWidth(r.GetWidth()*2); r.SetHeight(r.GetHeight()*2)
r.SetWidth(r.GetWidth()*2); r.SetHeight(r.GetHeight()*2)
-为实现,CSquare将两个大小增加4倍,而不是CRectangle假定的2倍。
恕我直言,这一原则表明了跟踪此类非正式约束的困难,这在开发方法“基础实现”的实用性和使用频率很高的情况下需要特别注意。
接口分离原理(ISP)接口分离原理; 依赖倒置原则(DIP)依赖倒置原则
这两个原则在其要求方面非常接近。 两者都隐含着使用最低限度的基本接口作为交互两个“组件”(“客户端”和“服务器”)的工具的有用性-选择这些名称只是为了标识。 在这种情况下,[组件]使用的一般信息集中在基本界面中。 一个[组件](“服务器”)实现基本接口的实现,另一个[组件](“客户端”)引用此实现。
这些原则的目标是最大程度地减少组件依赖性,如果不更改基础接口,则允许对其代码进行独立更改。 如果组件满足SRP原则的要求,组件更改的独立性将减少复杂性和劳动。 可能存在类似的方法,因为开发中存在以下不变式:
- 先前描述的[1],[2],[3],
- 行为固有的每个[组件]构成其使用的限制,
- 在[组件]的每个使用地点,都可能涉及其所有限制,
- 该定义的基本[component]结果比[component]实现具有更少的复杂性和限制数量,
- [component]的任何更改都会改变其局限性,并且需要验证其使用的所有位置,这会导致程序员的时间浪费,
在更改[component]实现后,不需要验证基础[component]的使用位置。
显然,建议通过丢弃未使用的功能和限制来最小化基本接口的“大小”,从而限制[component]原则(LSP)的实现
ISP的原则强调,如果该“客户端”未使用其所有已发布的功能,则需要对“服务器”的接口进行分离(隔离)。 在这种情况下,仅分配客户所需的[基本],并确保最小化共同限制信息。
在这里,我想写出一套维护DIP的技术。 在明显的地方,您可以找到这些技巧:
- 将类描述分为公共部分和私有部分(以及OOP的其他原则),
- 描述与具有有限功能和对象描述符的动态库的交互,
- 使用文件柜作为访问图书库的界面。
回到标题,我将解释为什么选择“不理解”。 添加否定是为了通过错误强调长期遭受痛苦并且非常恕我直言有用的规则。 最好不要误解,不使用技术,而不是误解,坚守信念,花费资源来使用该技术,因此,除了自满和吹嘘参与时尚技术的可能性之外,别无用处。
谢谢您的关注。
参考文献