软件构造课复习笔记【4】

4-1可复用性的度量、形态与外部表现

复用的级别:
源代码级别的复用
模块级别的复用:类/抽象类/接口
库级别的复用:API/包
系统级别的复用:框架

什么是软件复用

面向复用编程:开发出可复用的软件
基于复用编程:利用已有的可复用软件搭建应用系统

复用的作用:

降低成本和开发时间 
经过充分 测试,可靠、稳定 
标准化,在不同应用中保持一致 

但支持复用性越强的软件开发成本同时越高,性能差些: 针对更普适场景,缺少足够的针对性

使用已有软件进行开发:
可复用软件库,对其进行有效的管理
往往无法拿来就用,需要适配

如何度量可复用性

复用的机会有多频繁?复用的场合有多少?
复用的代价有多大?

获取
适配
实例化
与软件其他部分的互连的难度

易于复用的软件的特点:

小、简单 
与标准兼容 
灵活可变 
可扩展 
泛型、参数化 
模块化 
变化的局部性 
稳定 
丰富的文档和帮助

可复用组件的层次和形态

最主要的复用是在代码层面,但需求,spec,测试用例等等也可以复用。

白盒复用:源代码可见,可修改和扩展,需要对其内部充分的了解
黑盒复用:源代码不可见,不能修改 ,只能通过API接口来使用,无法修改代码 ,适应性差些

源代码复用

相关研究1:如何从互联网上快速找到需要的代码片段?
反向研究:如何从源代码中检测出克隆代码(clone code)?

模块级复用(类,接口)

继承
使用
组成/聚合
委托/关联

委托:一个对象依赖于另一个对象的部分功能
显式委托:将发送对象传递给接收对象
隐式委托:根据语言的成员查找规则

库级别的复用:API/包

库和框架的区别:
开发者构造可运行软件实体,其中涉及到对可复用库的调用
Framework作为主程序加以执行,执行过程中调用开发者所写的程序

系统级别的复用:框架

框架:一组具体类、抽象类、及其之间的连接关系,开发者根据 framework的规约,在Framework预留的接口填充自己的代码进去,形成完整系统

白盒框架,通过代码层面的继承进行框架扩展
黑盒框架,通过实现特定接口/delegation进行 框架扩展

可重用性的外部观察

类型可变 
功能分组 
实现可变 
表示独立 
共性抽取

类型可变(泛型):适应不同的类型,且满足LSP
实现可变:ADT有多种不同的实现,提供不同的representations和abstract function,但功能分组:具有同样的specification (pre-condition, postcondition, invariants),从而可以适应不同的应用场景
提供完备的细粒度操作,保证功能的完整性,不同场景下复用不同的操作(及其组合)
表示独立:内部实现可能会经常变化,但客户端不应受到影响。
共性抽取:将共同的行为(共性)抽象出来,形成可复用实体:父类、抽象类

4-2面向复用的软件构造技术

设计可复用的类 :
继承与重写
重载
参数多态与泛型编程
行为子类型 与Liskov替换原则 (LSP)
组合与委托

设计可复用的类

LSP

子类型多态:客户端可用统一的方式处理 不同类型的对象
例:设Cat是Animal的子类

都是对的。

LSP原则:

子类型可以增加方法,但不可删
子类型需要实现抽象类型 (接口、抽象类)中所有未实现的方法
子类型中重写的方法必须有相同或子类型的返回值或者符合co-variant的参数
子类型中重写的方法必须使用同样类型的参数或者符合contra-variant的参数(此种情况Java目前按照重载overload处理)
子类型中重写的方法不能抛出额外的异常

或者概括为:

更强的不变量 
更弱的前置条件 
更强的后置条件

子类型方法参数:逆变
子类型方法的返回值:协变
异常类型:协变

协变:

父类型→子类型:越来越具体specific 
返回值类型:不变或变得更具体 
异常的类型:不变或变得更具体 

逆变:

父类型→子类型:越来越具体specific 
参数类型:不变或变得更抽象

目前Java中遇到这种情况(参数逆变和协变),当作overload看待(JAVA不支持)

Java中 数组是协变的: 对T[]数组,可以保存类型T及其子类型的数据

泛型是类型不变的 ,类型参数在编译后被丢弃, 在运行时不可用 。
即,泛型在编译时确定具体类型,在运行时虚拟机中已没有泛型概念。(类型擦除)

The class Class 类型类
Java在运行时,为所有对象维护一 个运行时类型标识,这个标识跟踪对象所属的类,用来确定选择哪个方 法运行。保存这些信息的类叫做“Class类型类”。注意:Class是类的名字,不是关键词class。每个”Class”的对象描述了一个类的信息。

泛型中的通配符:
无限定通配符? ,List<?>,泛型可以是任意类型
方法的实现不依赖于类型参数

<? super A> 下限通配符 :只能是A或A的祖先类 <? extends A> 上限通配符 :只能是A或A的子类 #### 委托和组成 如果你的ADT需要比较大小,或者要放入Collections或Arrays中 进行排序,可实现Comparator接口并override compare()函数。(委托机制) 另一种方法:让你的ADT实现Comparable接口,然后override compareTo() 方法 与使用Comparator的区别:不需要构建新的Comparator类,比较代 码放在ADT内部。(不是委托) 委派/委托:一个对象请求另一个对象的功能 委托是复用的一种常见形式 很多设计模式将继承和委托结合使用 如果子类只需要复用父类中的一小部分方法,可以不需要使用继承,而是通过委派机制来实现,从而避免继承大量无用的方法。 组合是委派的一种形式 “委托” 发生在object层面,而“继承”发生在class层面 CRP(Composite Reuse Principle):类通过组合(通过包含实现所需功能的其他类的实例)而不是从基类或父类继承来实现多态行为和代码重用。 实际操作:通过inheritance实现对某些通用行为的复用 缺点:需要针对一种操作设计复杂的继承关系树;不能同时支持针对另一种操作的继承;操作的需求为发生变化时, 继承树要随之变化。 委托的分类: (1) Dependency: 临时性的delegation 通过方法的参数或者在方法的局部中使用发生联系 (2) Association: 永久性的delegation 通过成员变量委托 (3) Composition: 更强的association,但难以变化 (4) Aggregation: 更弱的association,可动态变化 #### API设计 API是程序员最重要的资产,吸引外部用户,提高声誉 建议:始终以开发API的 标准面对任何开发任务,要面向“复用”编程 而不是面向“应用”编程 设计API的难度:要有足够良好的设 计,一旦发布就无法再自由改变 # 4-3面向复用的设计模式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值