一、度量可维护性
- 圈环复杂度
- 类的继承层次数
- 单元测试覆盖度
- 类之间的耦合程度
- 直接根据公式计算可维护性的指数
二、五个模块设计的基本准则
- 直接映射:和现实世界中的结构保持一致
- 更少的接口:模块尽可能少的与其他模块通讯
- 更小的接口:如果两个模块通讯应该交换尽可能少的信息
- 隐式接口:当A与B通讯时应该明显发生在A与B之间,中间不可以隐式地跟着另外一个对象
- 信息隐藏:经常变化地设计隐藏在抽象接口后面
三、内聚和耦合
耦合的定义:模块间依赖的一种度量。
- 具体的度量方式:接口的数量,接口的复杂度
内聚的定义:一种对于模块内部函数和责任相关性的度量
- 如图所示,不相关的东西放在一起,则为低内聚
- 如图所示,所有东西都朝着同一个目标而工作,一旦某个功能变化让其他跟着变化,是合理的
理想的实现方式:高内聚,低耦合。两者是一对矛盾
四、Solid设计原则
SRP单一责任原则
定义:ADT不应该有多余1个原因使其发生变化。否则就将其拆分开。
- 如图所示,如果rectangle中的draw发生变化则将导致左侧的Computational类发生变化,违反了SRP原则
- 于是,可以将draw和area方法拆分开来分别放在两个接口中
- 但是,不应该刻意的进行拆分。要考虑到将来的可扩展性
面向变化的开放封闭原则(OCP)
定义:不改变原有代码但可对其行为进行功能扩展。
- 关键方法:抽象技术。继承,组合和代理。
- 方案:设计抽象类。共有的方法在抽象类中实现,独有的方法继承抽象类,添加新方法。联合多态技术进行实现。
class GraphicEditor {
public void drawShape(Shape s) { //最终根据传入的情况,根据多态性自动调用子类中的方法
s.draw();
} }
class Shape {
abstract void draw(); //声明一个抽象类。共有的方法在抽象类中加以定义。具体的实现在子类中进行实现。
}
class Rectangle extends Shape {
public void draw() {
// 针对于每一种不同的子类,都
}
}
Liskov Substitution Principle(LSP替换原则)
定义:保证子类型可以替换父类型
ISP接口隔离原则
定义:不强迫client依赖不需要的接口,只提供必须的接口(实现了尽可能的内聚);要避免接口污染和胖接口。
- 有关于胖接口:聚合度低下,可以将它分解成多个接口,不同的接口向不同的客户端提供服务
- 有关接口污染的反例:
interface Worker {
void work();
void eat();
}
//这个接口不够聚合,例如如果是机器人的话是不需要吃东西的
修改:
interface Workable {
public void work();
}
interface Feedable{
public void eat();
}
interface Workman extends Workable,Feedable
{
}
interface RobotWorker extends Workable
{
}
Dependency Inversion Principle (DIP依赖倒置原则)
定义:高层模块不应该依赖于底层模块,二者都应该依赖于抽象;实现细节依赖于抽象
- 解读:高层模块policy layer仅能使用低层模块中的Mechanism layer,而Mechanism Layer又仅能使用Utility Layer;如果今后想要换一种实现形式,显然是非常复杂的。
- 解决方案:令Policy layer依赖于抽象,Mechanism也依赖于抽象。修改后的方案如图所示。当Policy layer需要更换一种实现时,直接更改传入为这个另一种实现的具体类即可。
- 换句话说,委托的时候要通过接口建立联系而不是具体子类建立联系。