可维护性的常见度量指标
***圈复杂度:衡量代码的结构复杂性,通过计算程序流程中不同代码路径的数量。具有复杂控制流程的程序将需要更多的测试来实现良好的代码覆盖率,并且将不易维护。
***代码行数:指代码中的行数。非常高的数值可能表明某种类型或方法试图做太多工作,应该将其分解为几个部分分别实现。同时,也表明类型或方法可能难以维护。
***可维护性指数:通过计算,得到一个0~100的索引值,用以表示维护代码的相对容易性,该索引值越大,表明程序越容易维护。该指数基于以下几点进行计算:Halstead卷(HC) + 环复杂性(CC) + 每个模块的平均代码行数(LOC)+ 每个模块注释行的百分比(COM)
计算公式:
***继承的层次数:指扩展到类层次结构根目录的类定义的数量。等级越深,就越难理解待定方法和字段在何处被定义或重新定义
***类之间的耦合度:通过参数、局部变量、返回类型、方法调用、泛型或模板实例化、基类、接口实现,在外部类型上定义的字段以及属性修饰来测量队唯一类的耦合
良好的软件设计应该具有高内聚性和低耦合度
高耦合度表明难以重用和维护(表明其与其他类型指尖存在很多相互依赖关系)
聚合度与耦合度
***耦合度:耦合指对模块之间的依赖关系的度量,如果两个模块之间的变化可能需要另一个模块的变更,则两个模块之间存在依赖关系。其基于以下两个因素:
①模块之间的接口数量(数量)②每个接口的复杂性(质量)
***聚合度:是衡量一个模块的功能或责任的强烈程度的一个指标。若一个模块内的所有元素都朝着相同的目标努力,那么它就具有很强的内聚力。
***最好的软件设计应该是具有高聚合度和低耦合度。
SOLID
*** (SRP) The Single Responsibility Principle 单一责任原则
不应有多于1个的原因使得一个类发生变化;一个类,一个责任
最简单的原则,却是最难做好的原则
*** (OCP) The Open-Closed Principle 开放-封闭原则
对扩展性的开放 + 对修改的封闭
模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化。
但模块自身的代码是不应被修改的;扩展模块行为的一般途径是修改模块的
内部实现
如果一个模块不能被修改,那么它通常被认为是具有固定的行为
关键的解决方案:抽象技术(inheritance和composition)
*** (LSP) The Liskov Substitution Principle Liskov替换原则
子类型必须能够替换其基类型,派生类必须能够通过其基类的接口使用,客户端无需了解二者之间的差异
*** (DIP) The Dependency Inversion Principle 依赖转置原则
高级模块不应该依赖于低级模块,两者都应该取决于抽象
抽象的模块不应依赖于具体的模块;具体应依赖于抽象
优点:将类合同正式化;根据前后置条件定义例行服务
*** (ISP) The Interface Segregation Principle 接口聚合原则
客户端不应依赖于它们不需要的方法,最主要的是解决“胖”接口,这是由于“胖”接口不够聚合。
方法:“胖”接口可分解为多个小的接口;不同的接口向不同的客户端提供服务;客户端只访问自己所需要的端口
设计模式
***factory method:也可被称为虚拟构造器
目的:定义创建对象的接口,但让子类决定实例化哪个类;
工厂方法让类将实例化推迟到子类
当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。
常规情况下,client直接创建具体对象:Product p = new ProductTwo();
在工厂方法模式下:Product p =new ConcreteTwo().makeObject();
优点:①消除了将特定于应用程序的类绑定到代码的需要
②代码仅处理Producton接口,因此它可以与任何用户定义的ConcreteProduction实现
缺点:①client必须创建Creator的子类,以便他创建ConcreteProduct具体实现类
②client可以处理Creator的子类,但若不能,client必须处理赢一个进化点
原则:Open-ClosedPrinciple (OCP)
***Abstract Factory
抽象工厂模式:提供接口以 创建一组相关/相互依赖的对象,但不需要指明
其具体类。其创建的不是一个完整产品,而是“产品族”(遵循固定搭配规
则的多类 产品的实例 ),得到的结果是:多个不同产品的object ,各产品
创建过程对client 可见,但“搭配”不能改变
目的:允许独立于实现的创建相关对象
方法:使用工厂返回可用于创建相关对象集的工厂
用例:一个UI ,包含多个窗口控件,这些控件在不同的OS 中实现不同
一个仓库类,要控制多个设备,这些设备的制造商各有不同,控制接口有差异
本质上,Abstract Factory是把多类产品的factory method组合到一起
方法:使用组合/委派
***Builder
创建复杂对象,包含多个组成部分
创建的是一个完整的产品,有多个部分组成,client不需了解每个部分是怎
么创建、各个部分怎么组合,最终得到一个产品的完整object
目标是“创建复杂对象”,灵活扩展:
-将一个复杂对象的构造方法与对象内部的具体表示分离出来,同样的构造
方法可以建立不同的表现。
-不强调复杂对象内部各部分的“次序”
-子类override复杂对象内部各部分的“创建”
-适应变化:通过派生新的builder来构造新的对象(即新的内部表示),OCP
***Bridge
Bridg是OOP最基本的structural pattern ,通过delegation+inheritance
建立两个具体类之间的关系(DIP 依赖转置,抽象依赖于抽象
强调双方的run time delegation linking
***Proxy
某个对象比较“敏感”/“私密”/“贵重”,不希望被client直接访问到,故设置proxy,在二者之间建立防火墙
目的:隔离对复杂对象的访问,降低难度/代价,定位在“访问/使用行为”
***Composite
目的:在同类型的对象之间建立起树型层次结构,一个上层对象可包含多个下层对象
***Observer
优点:①在subject和observer之间有低内聚性
②可以动态添加和删除
③observer在计算时不受主体控制
***Visitor
对特定类型的object的特定操作(visit),在运行时将二者动态绑定到一起,
该操作可以灵活更改,无需更改被visit
本质上:将数据和作用于数据上的某种/ 些特定操作分离开来
在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate
到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而
不影响ADT
***Mediator
多个对象之间要进行交互,不是直接交互,而是通过mediator,实现消息的广播,从而将对象之间松散耦合,利于变化
一组同类型的object,彼此之间相互发/收消息(多对多),不存在谁观察谁的问题,所有object都对等。每个object都维持一个mediator,将通讯的任务delegate给它,自己只负责send和receive即可,无需了解其他object,实现了“解耦”。
Mediator:第三方中介负责广播(类似于“邮件列表”)
***Command
将“指令”封装为对象,指令的所有细节对client隐藏,在指令内对具体的ADT发出动作(调用ADT的细节操作)
Problem:客户端希望执行指令,但不想知道指令的细节,也不想知道指令的具体作用对象
强调将指令封装为了“对象”,提供了统一的对外接口
两个设计模式
***Memoto
目的:记住对象的历史状态,以便于“回滚”
三个职责:Originator--需要“备忘”的类
Caretaker--添加originator的备忘记录和恢复
Memento--备忘录,记录originator对象的历史状态
***State
使用有限状态机来定义程序的行为、使用状态来控制程序的执行
根据当前状态,决定下一步要执行什么操作、执行操作之后要转移到什么新的状态
核心思想:将程序看作是一个有限状态自动机,侧重于对“状态”及“状态转换”的抽象和编程
程序的执行被分解为一组自动执行的步骤,各步骤之间的通讯通过“状态变
量”进行
实现方式:程序执行就可看作是各自动步骤的不断循环
使用枚举类型enum定义
使用二维数组定义状态转换表
语法 正则表达式
***语法
定义:是一系列的产生式节点,其每一个都定义了一个非终止节点
非终止节点:遵循特定规则,利用操作符、终止节点和其他非终止节点,
构造新的字符串
***正则表达式
定义:减少终止节点的表达式,并且将操作符以更紧凑的形式编写