[转]设计的核心任务之三:确保正交性
原文:https://www.cnblogs.com/daoshi/archive/2012/05/30/2524972.html
http://tommwq.tech/blog/2020/11/19/223
如何度量正交性
目前没有很好的度量正交性的方法。考虑到正交性意味着低耦合度,而耦合度是软件模块之间的感知程度,我们可以用下面的公式来描述正交性:
orthogonality = n*(n-1)/k
其中n是软件中模块的数量,k是模块之间依赖关系的数量。如果k=0,我们将正交性定义为正无穷大。
假设软件包含n个模块,这些模块按照依赖关系构成了一个有向图。当每个模块都依赖于其他模块时,依赖关系数量最多,是n*(n-1)个。当各模块互不依赖时,依赖关系数量是0。我们用k表示模块依赖关系数量,显然k衡量了模块之间的耦合度。k的值域依赖于模块数n,我们对k做归一化处理,让k除以n*(n-1),就得到了值域是[0,1]的耦合度度量函数。考虑到正交性和耦合度呈类似反比例的关系,再取倒数就得到了上面的公式。
-----------------------------------------------------------
写面向对象设计原则的文章很多,但在我看来面向对象的一些原则是虽然是对的,但不够精练。
大多面向原则其实可以用三个支撑点推导出来:确保正交,控制层次,信息隐藏。
这一篇里谈一下确保正交性。
抽象是设计工作的起点,而抽象的结果可以是一个具体的概念,也可以是一段逻辑。正交性则与抽象的结果有关联。为了理解正交性,我们先来看一下这个词的几何解释:
当两根直线互相垂直的时候,我们认为这两根直线是正交的,否则的话这两根直线就是不正交的。
这似乎和软件没什么关联。
但如果我们假设相交的不是两根直线,而是两根圆柱的话,那么我们就可以看出来正交和非正交的差别所在。在正交的情况下,两根圆柱的最大接触面积始终会等于圆柱截面的面积,但在非正交的情形下,接触面积则要大于圆柱截面的面积,并且倾斜度越大,接触面积越大。
如果这两根圆柱是木材的话,那么接触面积越大,施工量越大,木材的可替换性也就越差。
概念或逻辑关系正交与否,其影响与上述类似。
假设说我们定义了两个类,类XMLReader负责具体读取XML文件中的节点,类XMLDataHandler负责加工从XML文件中读取出来的数据。这个时候如果在XMLDataHandler中出现了根据XPath读取XML内容的方法,那么这两个类无疑的会变成非正交的。因为读取这一功能即存在于XMLReader,也存在于XMLDataHandler。这种情形下,这两处地方都和XML的结构产生耦合,如果XML的结构发生变更,那么这两个地方都需要变更。
再假设说,我们在读取XML的时候要进行数据完整性的检查,并实现了xmlDataVerify()方法,那么如果在xmlRead()方法里面也进行了数据完整性的检查,那么这两个方法也是非正交的。因为检验规则必须要在两处同时存在。一旦检验规则发生变化,两个地方也需要同时被修改。
上述这类不正交的情况,有时候会被称为耦合,有时候会被称为不充分的抽象,但不管怎样,其根本问题在于概念或逻辑的非正交性。
不正交的情形有很多,但总结起来,这些情形大致可以分为两个类别,这两个类别与软件概念间可能的基本关系有关。
如果要把软件中的概念间的基本关系做个分类的话,那么大致可以分为两类:一为明确一种层次关系,不同的部分做的事情事实上是重叠的,但具体的程度不同,我们把这种关系称为横向分割,二为明确彼此关系,即你做什么,我做什么,我们把这种关系称为纵向分割。
横向分割产生“层”的概念。
比较经典的例子有OSI的网络模型,Windows的GDI设计等。这里以Windows的GDI设计来做一些说明:
Windows一直强调一个所见即所得的概念("WYSIWYG"),也就是说屏幕上用户看到的内容应该和打印机上打出来的内容保持一致。
如果应用程序(比如Word)与显示器的特性,乃至打印机的特性直接相关,那么几乎没可能达成这一目标。为解决这一问题,Windows中采用的办法是在具体设备和应用之间架起一个新的层次,这个新的层次即GDI。
这种情况下,GDI层和Driver层做的事情本质相同:即向指定页面描述指定内容。但具体描述方法不同,GDI较少关注设备特性(或者说只关注设备通用特性),而驱动程序则要关注设备的特有属性。
很多设计手法,其本质都是在软件的结构中加入更多的层次。像我们常说的Proxy, Facade模式,实现“开-闭”原则 (Open-Closed principle, OCP)的方法等。
纵向分割则产生模块或对象,经典的例子是MVC等模式。Model,View和Controller其实是不同的概念,但他们彼此间有联系,所以这三个相对独立的概念要经过某种关系连接在一起。
横向分割的时候,不正交体现为抽象层次上的不一致性,比如在Driver层面还做许多GDI层面应该做的事情;纵向分割的时候,不正交体现为重叠区域的存在,比如在View中直接对数据进行处理。
正交性强调的是只让概念或逻辑在必须关联的点上产生关联。
充分的抽象,其最终结果往往是正交的概念或逻辑,而正交的概念或逻辑大多时候是应对变化,可测试,降低耦合度的基础。