http://tommwq.tech/blog/2020/11/19/223
在几何学中,“正交”一词可以简单的理解为“垂直”。软件工程借用了这个术语,用来表示两个软件模块的耦合程度低。正交性高的软件更加灵活、更易于扩展和修改,拥有更长的生命周期、和更低的维护成本。
如果两个对象相互感知(关联、依赖)到对方,二者之间就发生了耦合。正交性就是低耦合性。当一个模块发生变更,与其耦合的模块也需要进行调整,以适配变更。正交性低的软件,需要适配的模块越多。即使一个很小的需求变化,也可能引发大量的代码修改。对于软件来说,修改意味着成本和(引入缺陷的)风险。如果放任成本和风险持续上涨,软件项目很快会变得无法推进,即无法应对需求变化,也无法修补缺陷。因此为了维持软件的竞争力,需要提高正交性。
提高正交性的根本方式就是时刻遵循单一职责原则。
There should never be more than one reason for a class to change.
Single Responsibility Principle
我们从一个例子来理解单一职责原则。
Listing 1: 违反单一职责原则
public class Printer {
public void print(String message) { /* ... */ }
public void print(String fmt, Object object) {
print(format(fmt, object));
}
public String format(String fmt, Object object) { /* ... */ }
}
如果软件需要支持一种新的打印格式,Printer.format方法就要发生改变。如果软件需要支持一种新的打印设备,Printer.print方法也要发生改变。有两个原因会引发Printer类的改变,这是因为Printer类承担了打印和格式化两个职责。如果采用单一职责原则,应当将打印和格式化职责分离。
Listing 2: 遵守单一职责原则
public class Formatter {
public String format(String fmt, Object object) { /* ... */ }
}
public class Printer {
public void print(String message) { /* ... */ }
public void print(String fmt, Object object) {
Formatter formatter = new Formatter();
print(formatter.format(fmt, object));
}
}
这是单一职责原则最简单也是最常见的应用。
这里提到的,不是持久层(数据库)的读写分离,而是在类层面,将读操作和写操作分离。这么做的好处有语义更清晰、读接口是幂等的、以及可以避免不必要的写操作。
Listing 3: 未分离读写接口
public class Calculator {
public int add(int value) { ... }
}
Listing 4: 分离读写接口
public class Calculator {
public int add(int value) { ... }
public int getResult() { ... }
}
Listing 5: 未分离创建和使用
public class Printer {
public void print(String message) { /* ... */ }
public void print(String fmt, Object object) {
Formatter formatter = new Formatter();
print(formatter.format(fmt, object));
}
}
Listing 6: 分离创建和使用
public class Printer {
public Printer(Formatter formatter) {
this.formatter = formatter;
}
public void print(String message) { /* ... */ }
public void print(String fmt, Object object) {
print(formatter.format(fmt, object));
}
}
参考
MVC、MVP和MVVM - tommwq.tech/blog
目前没有很好的度量正交性的方法。考虑到正交性意味着低耦合度,而耦合度是软件模块之间的感知程度,我们可以用下面的公式来描述正交性:
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]的耦合度度量函数。考虑到正交性和耦合度呈类似反比例的关系,再取倒数就得到了上面的公式。