admin管理员组文章数量:1579086
第九讲 面向复用的软件构造技术
不同级别的复用:
- 源代码级别的复用
- 模块级别的复用:类/抽象类/接口
- 库级别的复用:API/包
- 系统级别的复用:框架
设计可复用的类
- 继承和重写
- 重载
- 参数多态和泛型编程
- 行为子类型与Liskov替换原则(LSP)
- 组合与委托
设计可复用库和框架
- API和库
- 框架
1.什么是软件可复用性
面向复用编程:开发出可复用的软件
例如:开发了一个基于泛型的抽象接口Graph,定义了ADT
针对该ADT,用两种不同的Rep,开发了两个不同的实现ConcreteVertexGraph和ConcreteEdgeGraph
基于复用编程:利用已有的可复用软件搭建应用系统
利用该ADT和两个实现,完成了两个应用的开发
为什么可复用?
降低成本和开发时间
经过充分测试,可靠、稳定
标准化,在不同应用中保持一致
2.如何测定“可复用性”(了解)
小而简单;与标准兼容;灵活可变;可扩展;泛型、参数化;模块化;变化的局部性;稳定;丰富的文档和帮助
3.可复用组件的级别与形态
最主要的复用是在代码层面,但软件构造过程中的任何实体都可能被复用
源代码层面:方法,声明等
模块层面:类和接口
库层面:API
构建层面:框架
代码复用类型
白盒复用:源代码可见,可修改可扩展——继承
黑盒复用:源代码不可见,不能修改——委托
(1)源代码复用 (了解)
(2)模块层面复用:类/接口
复用一个类的方法:
1)继承
可以继承父类已存在的方法,也可重写已存在的行为
2)委托
一个对象为实现其功能的某个子集而依赖于另一个对象(一个实体将某物传递给另一个实体)
显示委托将发送对象传递给接受对象
隐式委托通过成员查找语言规则
(3)库层面复用:API/包
库:提供可复用功能的类和方法的集合
(4)系统层面复用:框架
框架:可定制为应用程序的可重用框架代码
一组具体类、抽象类、及其之间关系的连接关系
开发者根据framework的规约,填充自己的代码进去,形成完整系统
框架调用回客户端代码
白盒框架通过代码层面的继承进行框架扩展
黑盒框架通过实现特定接口/委托进行框架扩展
4.设计可复用类
在OOP中设计可重用类
封装和信息隐藏
继承和重写
多态性、子类型和重载
泛型编程
行为子类型和里氏替换原则
委托和组成
(1)行为子类型和里氏替换原则
行为子类型
子类型多态:客户端可以用同一的方法处理不同类型的对象
//Cat是Animal的子类
Animal a=new Animal();
Animal c1=new Cat();
Cat c2=new Cat();
在使用a的场景,都可以用c1和c2代替不会产生任何问题
a=c1;
a=c2;
//不会报错
(java中可通过静态检查)
- 子类型中可以增加方法,但不可以删除方法
- 子类型需要实现抽象类型中所有未实现方法
- 子类型中重写的方法必须有相同或子类型的返回值或者符合co-variance(协变)的参数
- 子类型中重写的方法必须使用同样类型的参数或者符合contra-variance(逆变)的参数
- 子类型中重写的方法不能抛出额外的异常
规约应符合(编译器检测不到,需自行检查)
- 相同或更强的不变量(子类实现相同的RI或增加新的RI)
- 相同或更弱的前置条件(子类重写方法时)
- 相同或更强的后置条件(子类重写方法时)
子类型的规约要更强,足以替换父类型的规约
LSP(强行为子类型化)
- 前置条件不能强化
- 后置条件不能弱化
- 不变量要保持
- 子类型方法参数:逆变
- 子类型方法的返回值:协变
- 异常类型:协变
协变
父类型——>子类型:规约越来越具体,返回值不变或变得更具体,异常不变或者变得更具体
反协变、逆变
子类型——>父类型:规约越来越具体;参数类型不变或更抽象
class T{
void c(String s){...}
}
class S extends T{
@Override void c(Object s){...}
}
目前Java遇到这种情况,会当做重载看待
数组是协变的,Number,List,Set支持协变
Number[] numbers=new Number[2];
number[0]=new Integer(10);
number[1]=new Double(3.14);
// 数组元素看具体实现,元素类型可以是Number的子类
Integer[] myInts={1,2,3,4};
Number[] myNumber=myInts;//myNumber数组元素已确定为Integer
myNumber[0]=3.14//运行时出错
泛型中的LSP
泛型类不支持协变,编译器会报错
ArrayList<String>是List<String>的子类
List<String>不是List<Object>的子类
List<Integer> myInts=new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums=myInts;//编译器报错
类型擦除
泛型的类型参数在运行时被具体的类替代
泛型中的通配符?
<? super A> :<>中可填A及其父类(父类的父类...) <? extends A>:<>中可填A及其子类 例如: List和List都是List<?>的子类型List ——>List<? extends Integer> ——> List<? extends Number> ——>List<?>
List ——>List<? super Number> ——>List<? super Integer> ——>List<?>
List ——>List<? extends Number>
List ——>List<? super Integer>
(2)委托
接口 Comparator<>
int compare( T o1,T o2)
如果你的ADT需要比较大小,或者放入Collections或Arrays进行排序,可实现Comparator接口并重写compare()函数
//需要比较的类
public class Edge{
Vertex s,t;
double weight;;
...
}
//构造用于实现比较的类
public class EdgeComparator implements Comparator<Edge>{
@Override
public int compare(Edge o1,Edge o2){
if (o1.getWeight() > o2.getWeight() )
return 1;
else if(o1.getWeight() == o2.getWeight() )
return 0;
else
return -1;
}
}
//排序函数
public void sort(List<Edge> edges){
Comparator comparator=new EdgeComparator();
Collections.sort(edges,comparator);
}
接口 Comparable
另一种比较方法,让你的ADT实现Comparable接口,然后重写compareTo()方法
与使用Comparator的区别是:不需要构建新的Comparator类,比较代码放在ADT内部。
public class Edge implements Comparable<Edge>{
Vertex s,t;
double weight;
...
public int compareTo(Edge o){
if(this.getWeight()>o.getWeighgt())
return 1;
else if(this.getWeight()==o.getWeighgt())
return 0;
else return -1;
}
}
委托
也叫做Composite Reuse Principe(CRP)
如果一个类不需要继承另一个类的全部方法,通过委托机制调用部分方法,从而避免大量无用的方法。
委托:两个类A和B,在B中(has_a or use_a)
继承:B(is_a)
委托发生在object层面,而继承发生在class层面
组合优于继承原则
例子:
interface Flyable{
public voif fly();
}
interface Quackable{
public void quack();
}
class FlyWithWings implements Flyable{
@Override
public void fly(){
System.out.println("fly with wings");
}
}
class Quack implements Quackable{
@Override
public void quack(){
System.out.println("quack like duck");
}
}
interface Ducklike extends Flyable,Quackable{} //接口的组合,定义了行为的组合
public class Duck implements Ducklike{//从组合接口中派生出具体类
Flyable flyBehavior; //委托
Quackable quackBehavior; //委托
void setFlyBehavior(Flyable f){//设置委托对象实例
this.flyBehavior = f;
}
void setQuackBehavior(Quackable q){//设置委托对象实例
this.quackBehavior=q;
}
//通过委托实现具体行为
@Override
public void fly(){
this.flyBehavior.fly();
}
@Override
public void quack(){
this.quackBehavior.quack();
}
}
Client code:
//对接口编程
Flyable f=new FlyWithWings();
Quackable q=new Quack();
Duck d=new Duck();
d.setFlyBehavior(f);
d.setQuackBehvior(q);
d.fly();
d.quack();
委托的类型
支持一对多的委托
-
依赖——临时性的委托
在客户端,将类a作为参数传入B的方法class Duck{ void fly(Flyable f){ f.fly(); } } Client code: Flyable f=new FlyWithWings(); Quackable q=new Quack(); Duck d=new Duck(); d.fly(f); d.quack(q);
-
关联——永久性的委托
类B属性中有类A的对象- 组合——更强的关联,紧绑定,难以变化
class Duck{
Flyable f =new FlyWithWings();//f不能指向其他地方,相当于前面加了一个final
void fly(){
f.fly();
}
}
Client code:
Duck d=new Duck();
d.fly();//只能调用FlyWithWings中的fly
- 聚合——更弱的关联,松绑定,可动态变化
class Duck{
Flyable f;//f可以指向任何对象
void Duck(Flyable f){
this.f=f;
}
void setFlyBehavior(f){
this.f=f;
}
void fly(){
f.fly();
}
}
Client code:
Flyable f=new FlyWithWings();
Duck d=new Duck(f);//调用void Duck(Flyable f)
d.fly();
d.setFlyBehavior(new CannotFly());//调用void setFlyBehavior(f)
d.fly();//调用void fly()
6.设计系统级别的复用API库和框架
白盒——继承
黑盒——委托
第10讲 面向可维护性的构造技术
1.软件可维护性和演化
软件可维护性的类别
纠错性
适应性
完善性
预防性
软件维护不仅仅是运维工程师的工作,而是从设计和开发阶段就开始了
2.可维护性的度量
可维护性的别名:可扩展性,灵活性,可适应性
模板模式,可管理性,支持性
一些常用可维护性的度量:
- 圈复杂度——测量代码结构复杂度,越复杂,白盒测试路径覆盖测试用例越多
- 代码行数
- 可维护性指数(MI)
- 继承的层次数
- 类之间的耦合度
- 单元测试的覆盖度
3.模块化设计和模块化原则
模块化编程:
高内聚——都是为了完成一个任务
低内聚——模块间联系薄弱
分离关注点——每个模块只完成一个功能
信息隐藏——不需要知道具体实现
(1)五个评价模块化程度的标准
可分解性——大的组合可分解为小的
可组合性——大的组合是由小的组成
可理解性
可持续性——发生变化时受影响范围最小
出现异常之后的保护——出现异常后受影响范围最小
(2)五个模块化设计规则
直接映射
尽可能少的接口
尽可能小的接口
显式接口
信息隐藏
(3)耦合和内聚coupling and cohesion
耦合:模块间的联系——继承和委托(委托包括关联和依赖)
内聚:每个类为同一功能服务
耦合越高,内聚越低
4.面向对象设计原则:SOLID
(SRP)单一责任原则
(OCP)开放-封闭原则
(LSP)liskov替换原则
(ISP)接口聚合原则
(DIP)依赖转置原则
SRP 单一责任原则
最简单的原则,确实最难做好的原则
一个类一个责任
不应有多于一个的原因使得一个类发生变化
OCP (面向变化的)开放-封闭原则
类应该是对扩展性开放,对修改封闭
可以扩展模块的行为,但是不能修给模块本身的代码
关键的解决技术:抽象技术
继承或者委托
LSP liskov替换原则
子类型必须能够替换其基类型
派生类必须能够通过其基类型的接口使用,客户端无需了解二者之间的差异
见第九讲
ISP 接口隔离原则
不能强迫客户端依赖于他们不需要的接口,只提供必需的接口
DIP 依赖转置原则
抽象的模块不应该依赖于具体的模块,具体应依赖于抽象
委托时,要通过接口建立联系,而非具体子类
5.语法驱动构造
终止节点、叶节点
无法再往下扩展,通常表示为字符串,用引号标注,如‘http’和‘:’
产生式节点、非终止节点
遵循特定规则,利用操作符、终止节点和其他非终止节点,构造新的字符串。
产生式的形式
非终止节点 ::=终止节点、非终止节点和操作符构成的表达式
根节点
操作符
- 连接 x : := y z
- 重复 x : := y*
- 选择 x : := y | z
后缀操作符*,?,+有最高优先级别,连接次之,选择最后
更多的操作符 - Optional(0或1发生)用 ?表示
x : := y? 一个x是一个y或者是一个空串 - 1或者更多次发生,用+表示
x : := y+ 和 x : := y y*等价 - 一个符号类[…],由[…]中任意一个字符组成的长度为一的串
x : := [a-c] 等价于 x : :=‘a’ | ‘b’ | ‘c’
x : := [aeiou] 等价于 x : := ‘a’ | ‘e’ | ‘i’ | ‘o’ | ‘u’ - 一个反向字符类[…],表示一个长度为一并且不由[…]中字符构成的字符串
x : :=[^ a-c] 等价于 x : := ‘d’ | ‘e’ | ‘f’ | ‘g’ | …
递归
正则语法
简化之后可以表达为一个产生式而不包含任何非终止节点
正则表达式 (简写为regex)
去除引号和空格,从而表达更加简洁
- . 任何一个单个字符
- \d 任何一个数字,等价于[0-9]
- \s 任何空格字符,包括space,tab,newline
- \w 任何单词字符,包括下划线,等价于[a-zA-Z_0-9]
- . , (, ) ,* ,+,… 对运算符或特殊字符进行转义,以便逐字匹配
Original:
' http:// ' ( [a-z]+ ' . ' )+ [a-z]+ ( ' : ' [0-9]+ ) ? ' / '
Compact:
http://([a-z]+.)+[a-z]+ (:[0-9]+) ?/
With escape:
http://([a-z]+\.)+[a-z]+ (:[0-9]+) ?/
Pattern是对正则表达式进行编译之后得到的结果
Macher:利用Pattern对输入字符串进行解析
PatternSyntaxException是unchecked异常,表明在正则表达式匹配时有语法错误
在java中使用正则表达式
用单个空格代替多个空格
String singleSpacedString=string.replaceAll(" +"," ");
匹配一个URL
Pattern regex=Pattern.compile("http://([a-z]+\\.)+[a-z]+ (:[0-9]+) ?/");
Mather m=regex.matcher(string);
if(m.matches()){
//then string is a url
}
Construct | Description |
---|---|
[abc] | ab,or c |
[^abc] | 除了a,b,c以外任一字母 |
[a-zA-Z] | a到z,或A-Z |
[a-d[m-p]] | 等价于[a-dm-p] |
[a-z&&[def]] | 等价于[def] |
[a-z&&[^bc]] | 等价于[ad-z] |
[a-z&&[^m-p]] | 等价于[a-lq-z] |
metacharacter元字符 | |
<([{^-=$! | ]})?*+.> |
两种将元字符当做普通字符使用的方式: |
- 在元字符前加\
- 用\Q和\E闭合
Construct | Description |
---|---|
. | 任何一个字符 |
\d | [0-9] |
\D | [^0-9]非数字 |
\s | 一个空格字符[ \t\n\x0B\f\r] |
\S | 一个非空格字符[^\s] |
\w | [a-zA-Z_0-9] |
\W | [^\w] |
Greedy | Reluctant | Possessive | Meaning |
---|---|---|---|
X? | X?? | X?+ | X,一次或没有 |
X* | X*? | X*+ | X,零或多次 |
X+ | X+? | X++ | X,一次或多次 |
X{n} | X{n}? | X{n}+ | X,重复n次 |
X{n,} | X{n,}? | X{n,}+ | X,至少n次 |
X{n,m} | X{n,m}? | X{n,m}+ | X,至少n次但不多于m次 |
版权声明:本文标题:软件构造第九讲和第十讲 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1727846473a1133149.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论