哈工大 软件构造课程 考点复习总结(第四、五章)
-
代码可理解性
-
度量
-
标识符的平局长度
-
命名独特性比例
-
代码复杂度
-
LoC 代码行数
-
注释密度(百分比)
-
-
如何书写可理解性代码
-
命名规范
-
代码行最大长度,文件最大LoC
-
注释
-
布局:缩进、对齐、空行、分块等
-
避免多层嵌套(增加复杂度)
-
文件和包的组织
-
-
包的相关原则
-
REP 复用/发布等价原则
复用的粒度等价于发布的粒度(发布的都是应被复用的,未发布的不应被复用)
-
CCP Common Closure Principle 共同封闭原则
-
一个包中的所有类针对同一种变化是封闭的
-
一个包的变化将会影响到包里所有的类,而不会影响到其他的包
-
如果两个类紧密耦合在一起,即二者总是同时发生变化,那么它们应属于同一个包
-
-
CRP Common Reuse Principle 共同复用原则
-
一个包里的类应被一起复用
-
如果复用了其中一个类,那么就应复用所有的类
-
总之,尽量把相关性高的类放在一起,对不同类进行合理划分。
-
-
代码复用
-
可复用组建的形态和层级
-
源代码:方法、声明等
-
模块:类、接口
-
库:API
-
系统(结构):framework 框架:一组具体类、抽象类及其之间的连接关系
-
-
代码复用的类型:
-
White box 白盒复用:源代码可见,可修改和扩展
即,直接复制修改代码。可定制化程度高,但需要充分了解代码。
-
Black box 黑盒复用:源代码不可见,不能修改
只能通过API接口使用,无法修改代码。简单、清晰,但适应性差。
-
-
Frameworks :domain-level reuse 领域复用
框架:一组具体类、抽象类及其之间的连接关系
开发者根据framework的规约,填充自己的代码进去,形成完整的系统;
开发者 增加新代码、对抽象类机型具体化、实现接口;
Framework作为主程序加以执行,执行过程中调用开发者所写的程序。
控制反转(inverse of control),控制权由代码转到了外部容器(framework),好处是降低了对象之间的依赖程度,提高灵活性和可维护性。
-
External observations of reusability 可复用的外部观察特性
-
Type variation 类型可变:泛型,且满足LSP
-
Implementation variation 实现可变:
ADT有多种不同的实现,提供不同的representations和abstract funtion,但具有同样的specification (pre-condition, post-condition, invariants),从而可以适应不同的应用场景
-
Routine grouping 功能分组:
-
Representation independence 表示独立
内部实现可能会经常变化,但客户端不应受到影响(不变量为true)
-
Factoring out common behaviors 抽取共性行为
-
-
LSP (Liskov substitution principle) liskov 替换原则
-
子类型可以增加,但不可删除方法
-
子类型需要实现抽象类中所有未实现的方法
-
子类型中重写的方法必须有相同或子类型的返回值 co-variance:协变
-
子类型中重写的方法必须使用同样类型的参数 contra-variance 反协变(逆变)
-
子类型中重写的方法不能抛出额外的异常 协变
-
同样或更强的不变量
-
同样或更弱的前置条件
-
同样或更强的后置条件
-
Covariance 协变、Contravariance 反协变,逆变
-
协变
父类型->子类型 |
Spec越来越具体 |
返回值类型 |
不变,或更具体 |
异常类型 |
不变,或更具体 |
-
反协变
父类型->子类型 |
Spec越来越具体 |
参数类型 |
不变,或更抽象(由于java语法,不能更抽象) |
-
注意
-
数组( T [ ] )是协变的
T[ ]中元素必须是T或其子类,否则编译时通过,但运行时会错误。
-
泛型 不变(类型擦除)
-
-
通配符 <?>
调用时使用。
-
Comparator and Comparator
对自定义ADT(Edge为例)比较大小,或要放入collections或arrays进行排序
-
创建比较类EdgeCompare实现comparator接口
Collections . sort ( Lsit <Edge> , EdgeCompare)
-
Edge类实现Comparable接口
类内ouvrride compareTo 方法
-
Delegation 委派
一个对象请求另一个对象的功能
如果子类只需要复用父类中的一小部分方法,可以不需要使用继承,而是通过委派机制实现。
即,一个类不需要继承另一个类的全部方法,通过委派机制调用部分方法。
"委派"发生在object层面,"继承"发生在class层面
委派的类型
-
Dependency 依赖 临时的委派 (B uses A)
类内调用
A类是B类中的(某中方法的)局部变量;
A类是B类方法当中的一个参数;
A类向B类发送消息,从而影响B类发生变化;
-
Association 关联 永久的委派 (B owns A) (关联关系可以是双向的)
A类为B的成员变量(或者对象数组)。
也可能自关联(如,自身的成员变量有自身的实例,如父节点有子节点)
-
Composition 组合 更强的委派 (A is part of B )
A是B的一部分,B不能脱离A存在,A不能单独存在。
-
Aggregation 聚合
整体和部分(可有可无)的关系。
-
CRP (Composite Reuse Principle)组合复用原则
对于某一具体行为,用委派代替继承。
使用接口定义不同方面的行为,
接口之间通过extends实现行为的组合(组合多用接口,则组合接口汇集了其他接口的行为)。
用具体类实现组合接口。
-
White box and black box framework 白盒框架和黑盒框架
白盒框架
黑盒框架
实现方式
通过 子类 和 重写方法 实现扩展
继承
通过实现 插件接口 实现扩展
委派/组合
常用设计模式
模板方法
策略模式 观察者模式
调用机制
子类型有main方法,但framework拥有控制权
插件加载机制加载插件(委派),framework拥有控制权
另一种实现
在抽象父类中添加abstract方法
(需修改framework代码)
子类实现abstract方法
实现预留接口
(不需修改framework代码,framework中有预留接口)
实现类实现接口
例如
Class WhiteRun extends Thread{
(override run) }
New WhiteRun().strat
例如
Class BlackRun implements Runnable
New Thread( new BlackRun() ).start()
-
设计模式
Structural patterns 结构型设计模式
-
Adapter 适配器模式
将某个类/接口 转换为client期望的其他形式(client的调用传参与类/接口 方法的参数列表不一致,不兼容)
增加一个接口,将已存在的子类封装
Client面向接口编程,从而隐藏具体子类。
适配器类实现接口或继承实现接口的父类,
适配器中调用子类方法实现功能(委派)。
可能client调用子类方式(如参数形式等)与子类不匹配,
通过适配器进行调整调用,适配子类,从而实现原本不兼容的调用。
-
Decorator 修饰器
为对象增加不同侧面的特性。
对每一个特性构造子类,通过委派机制增加到对象上。
例如:Collections.synchronizedList( List<T> list)
即返回一个在list增加了新特性的对象。
即为对象本身和其需要增加的特性进行封装,
基础功能通过委派(父类)实现,并额外增加新特性,成为一个新的类。
Client可以通过层层修饰获得具有多种特性的对象。
-
Façade 外观模式
客户端需要通过一个简化的接口来访问复杂系统内的功能。
提供一个统一的接口来取代一系列小接口调用,相当于对复杂系统进行封装,简化客户端使用。
Behavioral patterns 行为类设计模式
-
Strategy 策略模式
对一个任务,有多用算法实现。
为该任务创建一个接口,
实现类实现同一接口中的方法。
-
Template method 模板模式
做事情的步骤一样,但具体方法不同。
共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现。
使用继承和重写实现模板模式。
(实验中的 GoncreteGraph)
模板模式广泛应用于framework。
-
Iterator 迭代器
独立于元素类型,访问容器内的所有元素。
Iterator pattern:让自己的集合类实现Iterable接口,并实现自己独特的iterator迭代器(hasNext,next,remove),允许客户端利用这个迭代器进行显示或隐式的迭代遍历。
(实现了iterable的对象才可用foreach遍历)
for(E e : collection) { …}
Iterator<E> iter = collection.iterator();
While(iter.hasNext()) {…}