层次分解
您永远不应该立即开始为您的应用程序编写类。首先需要对其进行设计。设计应该以深思熟虑的架构结束。要获得此架构,您需要始终如一地分解系统。
分解必须分层进行——首先,系统被分成大的功能模块/子系统,以最一般的形式描述其操作。然后对生成的模块进行更详细的分析,并将其划分为子模块或对象。
在选择对象之前,将系统分成基本的语义块,至少在心理上是这样。在小型应用程序中,这通常很容易做到:几个级别的层次结构就足够了,因为系统首先分为子系统/包,而包又分为类。
![层次分解](https://cdn.codegym.cc/images/article/d5e9aa2b-5783-430f-b5b5-1832bc33c540/800.jpeg)
这个想法并不像看起来那么微不足道。例如,模型-视图-控制器(MVC)这种常见的“架构模式”的本质是什么?
这一切都是关于将表示与业务逻辑分开。首先,任何用户应用程序都分为两个模块——一个负责实现业务逻辑本身(模型),第二个负责与用户交互(用户界面或视图)。
然后事实证明,模块必须以某种方式交互,为此他们添加了一个控制器,其任务是管理模块的交互。同样在移动(经典)版本的MVC中,加入了Observer模式,使得View可以接收来自模型的事件,实时改变显示的数据。
典型的顶级模块,作为系统第一次划分为最大组件的结果,准确地说是:
- 商业逻辑;
- 用户界面;
- 数据库;
- 消息系统;
- 对象容器。
第一次拆分通常将整个应用程序拆分为 2-7(最多 10 个部分)。如果我们把它分解成更多的部分,那么就会有对它们进行分组的愿望,我们又会得到 2-7 个顶层模块。
功能分解
最好根据系统解决的任务来划分模块/子系统。主要任务分为其组成的子任务,这些子任务可以相互独立地自主解决/执行。
每个模块应负责解决一些子任务并执行其相应的功能。除了功能目的之外,模块的特征还在于它执行其功能所必需的一组数据,即:
模块 = 函数 +执行它所需的数据。
如果正确地分解成模块,那么与其他模块(负责其他功能)的交互将是最小的。它可能是,但它的缺失对你的模块来说并不重要。
模块不是任意一段代码,而是一个单独的具有功能意义和完整的程序单元(子程序),它为特定任务提供解决方案,理想情况下,可以独立工作或在另一个环境中工作并被重用。模块应该是一种“能够在行为和发展上相对独立的完整性”。(克里斯托弗·亚历山大)
因此,有效的分解首先基于对系统功能和执行这些功能所需数据的分析。这种情况下的函数不是类函数和模块,因为它们不是对象。如果一个模块中只有几个类,那么你就做得太过了。
强连接和弱连接
非常重要的是不要过度模块化。如果你给一个初学者一个整体的Spring应用,让他把它分解成模块,那么他会把每个Spring Bean拿出来放到一个单独的模块中,并认为他的工作完成了。但事实并非如此。
分解质量的主要标准是模块如何专注于解决它们的任务并且是独立的。
这通常表述如下:“作为分解结果获得的模块应该在内部最大程度地共轭(高内部凝聚力)并且最小程度地相互连接(低外部耦合)。”
High Cohesion,模块内部的高内聚或“内聚”,表示该模块专注于解决一个狭义的问题,而不是从事执行异构功能或不相关的职责。
内聚性表征模块执行的任务之间相互关联的程度。
高内聚的结果是单一责任原则——五个 SOLID 原则中的第一个,根据该原则,任何对象/模块都应该只有一个责任,并且改变它的理由不应该超过一个。
Low Coupling,松散耦合,是指系统划分成的模块之间应该尽可能独立或松散耦合。他们应该能够互动,但同时尽可能少地了解彼此。
每个模块都不需要知道另一个模块是如何工作的,它是用什么语言编写的,以及它是如何工作的。通常,为了组织这些模块的交互,使用特定的容器,将这些模块加载到其中。
通过适当的设计,如果您更改一个模块,则不必编辑其他模块,或者这些更改将是最小的。耦合越松,就越容易编写/理解/扩展/修复程序。
人们认为,设计良好的模块应该具有以下特性:
- 功能完整性和完备性——每个模块实现一个功能,但实现得好、完整,模块独立执行全套操作来实现其功能。
- 一进一出——在输入处,程序模块接收到一组初始数据,进行有意义的处理并返回一组结果数据,即实现了标准的IPO原则——输入-\u003e过程-\u003e输出。
- 逻辑独立性——程序模块的工作结果只依赖于初始数据,而不依赖于其他模块的工作。
- 与其他模块的信息链接较弱——如果可能,应尽量减少模块之间的信息交换。
初学者很难理解如何进一步减少模块的连通性。这种知识部分来自于经验,部分来自阅读智能书籍之后。但最好分析现有应用程序的架构。
组合而不是继承
胜任分解是一门艺术,对大多数程序员来说都是一项艰巨的任务。简单在这里具有欺骗性,错误代价高昂。
![](https://cdn.codegym.cc/images/article/d7e785ed-e16d-4354-883c-0ad83327512a/512.jpeg)
恰好专用模块之间强耦合,无法独立开发。或者不清楚他们各自负责什么功能。如果您遇到类似的问题,那么很可能是模块划分不正确。
应该始终清楚每个模块扮演什么角色。正确完成分解的最可靠标准是模块是否是独立且有价值的子例程,可以与应用程序的其余部分隔离使用(因此可以重用)。
分解系统时,最好通过问自己以下问题来检查其质量:“每个模块执行什么任务?”、“测试模块有多容易?”、“是否可以单独使用这些模块还是在另一个环境?影响他人?
您需要尽量保持模块的自主性。如前所述,这是正确分解的关键参数。因此,必须以模块之间最初弱依赖的方式进行。如果你成功了,那你就很棒了。
如果没有,那么这里也不会丢失所有内容。有许多特殊的技术和模式可以让您进一步减少和削弱子系统之间的联系。例如,在 MVC 的情况下,观察者模式用于此目的,但其他解决方案也是可能的。
可以说,解耦技术构成了主要的“架构师工具包”。只需要明白,我们说的是所有的子系统,需要弱化层次结构各个层级的联系,即不仅是类之间,还包括各个层级模块之间的联系。