软件构造(九) 面向复用的软件构造技术
1.什么是软件复用
软件重用是使用现有软件组件实现或更新软件系统的过程。
软件复用有两方面:
- 面向复用编程:开发出可复用的软件
- 基于复用编程:利用已有的可复用软件搭建应用系统
为什么复用:
- 降低成本和开发时间
- 经过充分测试,可靠、稳定
- 标准化,在不同应用中保持一致
2.如何衡量“可重用性”?
复用的代价:
- 搜索、获取库的代价
- 适配、扩展的代价
- 实例化的代价
- 与软件其他部分互联的代价
可重用性意味着对构建、打包、分发、安装、配置、部署、维护和升级问题进行明确的管理
一个软件库具有高度可重用性应具有以下特点:
- 规模小且简单
- 与标准兼容
- 灵活可变
- 可扩展
- 泛型化,参数化(不需要用户考虑实现)
- 模块化
- 变化的局部性
- 变化需求下的稳定性
- 丰富的文档和帮助
3.可重用组件的层次和形态
重用级别:
- 代码(最主要的)
- 需求
- 设计、规约
- 数据
- 测试用例
- 文档
几乎所有有关的一切都能拿来复用
3.1 源代码复用
源代码复用是最低级别
类型:
白盒复用
当代码本身可用时重用代码。通常需要某种修改或适应。于是,就把源码Ctrl+C并Ctrl+V,这是复用
好处: 可以自定义,以便于适应不同的情况
坏处: 增加了代码的复杂性,而且你也不一定能看的源码黑盒复用
通过API接口来使用,无法修改代码
好处: 简单,清晰
坏处: 适应性差
通常都是将源代码组织为库的形式,来进行复用的传播
可重用软件组件的来源:
- 内部(公司)代码库(Guava)
- 第三方库(Apache)
- 语言自身提供的(JDK)
- 来自教程、示例、书籍等的代码示例。
- 代码专家或知识渊博的同事
- 现有系统代码
- 开源软件
代码搜索网站:
- grepcode.com
- github.com/search
- searchcode.com
杜绝抄袭
3.2 模块级复用:类/接口
类是代码复用的组成单元,不需要源代码,需要一个压缩包jar/zip、一个使用文档
这样的封装是有利于复用的,优点在于管理更少的代码,但是版本控制,向后兼容性仍然存在问题
使用时之将相关类进行静态链接
复用类的方法:
继承(inheritance)
类扩展了现有类的属性/行为,同时还有可能覆盖(override)父类的内容
通常需要在实现之前设计继承层次结构
注意:不能删除一些属性或方法,因此必须小心不要对父类做出太多修改委托(delegation)
是一个对象调用另一个对象来实现其功能
委托可以被描述为在实体之间共享代码和数据的一种低级机制
3.3 库级复用:API/包
库(Library):包含可复用功能的一组类和方法(API)
框架(Framework):可复用的框架代码,可以自定义到应用程序中
两者的实现方式是不同的:
使用库,是你写出程序框架,然后用库中的方法和类去填充
使用框架,是你在框架的基础上,去设计填充的代码
一个好的API应该有以下特点:
- 易学
- 易于使用,即使没有文档
- 不易滥用
- 易于阅读和维护使用它的代码
- 足够强大以满足要求
- 易于进化
- 适合使用者
介绍一些Java库级复用:
Guava
Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存 [caching]、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解[common annotations] 、字符串处理 [string processing] 、I/O 等等
Apache Commons:
Commons是一个主要专注于可再用Java组件所有方面的Apache项目。 Apache Commons项目由3个部分组成:
*The Commons Proper—可再用Java组件的贮存处;
*The Commons Sandbox—Java组件的开发工作区;
*The Commons Dormant—沙盒组件的贮存库。
3.4 系统级重用:框架
框架(Framework):框架是包含抽象和具体类以及每个类之间的接口的集合的子系统设计。框架是一种抽象,其中提供通用功能的软件可以通过附加的用户编写的代码有选择地更改,从而提供特定于应用程序的软件
将framework看作是更大规模的API复用,除了提供可复用的API,还将这
些模块之间的关系都确定下来,形成了整体应用的领域复用
Framework作为主程序加以执行,执行过程中调用开发者所写的程序;开发者根据Framework预留的接口所写的程序
同样框架也可分为:
白盒框架
通过继承和动态绑定实现可扩展性
通过子类化框架基类和重写预定义的钩子方法(当触发了某个条件执行的方法)来扩展现有功能
通常使用模板方法模式等设计模式来覆盖钩子方法黑盒框架
通过为可插入框架的组件定义接口来实现可扩展性
通过定义符合特定接口的组件来重用现有功能
这些组件通过委托与框架集成
它与库的实现方式是不同的:
使用库,是你写出程序框架,然后用库中的方法和类去填充
使用框架,是你在框架的基础上,去设计填充的代码
4.设计可复用类
OOP中的可复用类设计,要做到以下几点:
- 封装与信息隐藏
- 继承和重写
- 多态性、子类和重载
- 泛型程序设计
- 行为亚型与Liskov替代原理(LSP)
- 委托和组成
4.1 LSP
子类型多态(Subtype polymorphism): 客户端可以统一处理不同类型的对象
举个例子,假设cat是animal的子类,那么cat可以用在所有出现animal的地方
Liskov替换原则: 如果对于类型T的对象x,q(x) 成立,那么对于类型T的子类型S的对象y,q(y) 也成立
根据著名的老色……,啊LSP,我们可以定义子类的概念,于是在Java中为了满足这一原则我们有:
Java中编译器强制执行的规则(静态类型检查):
- 子类型可以增加方法,但不可删
- 子类型需要实现抽象类型 (接口、抽象类)中所有未实现的方法
- 子类型中重写的方法必须有相同或子类型的返回值或者符合co-variant的参数
- 子类型中重写的方法必须使用同样类型的参数或者符合contra-variant的参数(此种情况Java目前按照重载overload处理)
- 子类型中重写的方法不能抛出额外的异常
也适用于指定的行为(方法):
- 更强的不变量
- 更弱的前置条件
- 更强的后置条件
LSP是子类型关系的一个特殊定义,称为强化行为子类型
在编程语言中,LSP依赖于以下限制:
- 前置条件不能强化
- 后置条件不能弱化
- 不变量要保持
- 子类型方法参数:逆变
反协变、逆变(Contravariance)
父类型→子类型:越来越具体specific
参数类型:要相反的变化,要不变或越来越抽象
- 子类型方法的返回值:协变
协变(Covariance)
父类型→子类型:越来越具体
返回值类型:不变或变得更具体
异常的类型:也是如此
- 异常类型:协变
那么是否也能满足LSP呢,由于在使用泛型时,会发生类型擦除(type erasure)。因此, 泛型是类型不变的,所以,泛型不是协变的。因此,List<String>
不是List<object>
类型擦除(type erasure):
泛型信息只存在于编译阶段,在运行时会被”擦除”
定义泛型类型时,会自动提供一个对应的原始类型(非泛型类型),原始类型的名字就是去掉类型参数后的泛型类型名。
擦除时类型变量会被擦除,替换为限定类型,如果没有限定类型则替换为Object类型。
无限定通配符 是使用通配符(?)指定的,例如List<?>。
适用场景:
情况1:方法的实现不依赖于类型参数(不调用其中的方法),如List中的方法;
情况2:或者只依赖于Object 类中的功能
<? super A> 下限通配符 :匹配A及A的父类、
<? extends A> 上限通配符 :匹配A及A的子类
使用上述三种通配符,可以使得原来不满足LSP的泛型,满足LSP:
List<Number> is a subtype of List<?>List<Number> is a subtype of List<? extends Object>List<Object> is a subtype of List<? super String>
类型类(The class Class): Java在运行时,为所有对象维护一个运行时类型标识,这个标识跟踪对象所属的类,用来确定选择哪个方法运行。保存这些信息的类叫做“Class类型类”。
4.2 委托和复合
先来介绍Java中一个实用的接口Interface Comparator<T>
。如果你的ADT需要比较大小,或者要放入Collections或Arrays中进行排序,可实现Comparator接口并override compare()函数
另一种方法:让你的ADT实现Comparable接口,然后重写compareTo() 方法
通过以上两种不同的使用Comparable接口的方法我们可以了解到,委托(Delegation) 与复合(Composition) 的区别
委派/委托(Delegation): 一个对象请求另一个对象的功能
就像上面第一种所展示的那样。
委托被描述为在实体之间共享代码和数据的一种低级机制
显式委托:将发送对象传递给接收对象
隐式委托:根据语言的成员查找规则
很多设计模式将继承和委托结合使用。如果子类只需要复用父类中的一小部分方法,可以不需要使用继承,而是通过委派机制来实现,从而避免继承大量无用的方法
组合优于继承原则(Composite over inheritance principle): 或称为CRP。一个类应该通过组合而不是从基类或父类继承来实现多态行为和代码重用。组合是委派的一种形式
委派可以分为三类:
- 依赖(Dependency)
- 关联(Association)
- 复合/聚合(Composition/aggregation)
可以认为复合/聚合是关联的两种具体形态
4.2.1 依赖(Dependency)
使用类的最简单形式是调用它的方法
依赖是将另一个类作为参数,或是在方法中局部使用,这种关系也被称为“uses-a”,这种关系是临时的,当方法调用结束,另一个类的生命也随之结束
4.2.2 关联(Association)
对象类之间的持久关系,一个类将另一个类作为属性或是实例变量,这种关系也被称为“has-a”
这种关系持续到调用类的结束
4.2.3 复合(Composition)
是一种将简单对象或数据类型组合成更复杂对象或数据类型的方法,一个类将另一个类作为属性/实例变量,实现为一个对象包含另一个对象。也被称为“is_part_of”
对象x的生命期由唯一拥有者owner控制,例如Engine是Car的一部分。脱离Car的Engine是没有实在意义的
4.2.4 聚合(Aggregation)
对象存在于另一个外部,在外部创建,因此它作为参数传递给构造函数也被称为,“has_a”关系
是一种特殊的联系,它表明了“部分”到“整体”的关系。例如Person有一个Address,但是Addess的存在是不依赖Person的
软件构造(九) 面向复用的软件构造技术相关推荐
- 软件构造学习笔记(九)面向复用的软件构造技术
目录链接 Part I What is Software Reuse? Part II How to measure "reusability"? Part III Levels ...
- 4-2 面向复用的软件构造技术
1.设计可复用的类 1.1 行为子类型和LSP 子类型: 相比于父类型,要有相同或更强的ADT(前置条件变弱或后置条件变强.更强的不变量): 在java中表现为:子类型可以增加方法:子类型中重写的 ...
- 面向复用的软件构造技术知识点总结与思考
一.复用 1.复用种类: 白盒复用:源代码可见,复制已有代码到正在开发的系统,进行修改和扩展 黑盒复用:源代码不可见,不能修改,只能通过API接口来使用 1)源码级别复用 2)模块级别复用:类,接口 ...
- 软件构造 第二章 第一节 软件生命周期和版本控制
软件构造第二章 第一节 软件生命周期和版本控制 基本内容 Software Development Lifecycle (SDLC) Traditional software process mode ...
- 软件构造 第一章第二节 软件开发的质量属性
软件构造 第一章第二节 软件开发的质量属性 1.软件系统质量指标 External quality factors affect users 外部质量因素影响用户 Internal quality ...
- 设计模式:可复用面向对象软件的基础
文章目录 设计模式 设计模式有4个基本要素 23个设计模式 设计模式分类 目的准则 范围准则 导致重新设计的一般原因,以及解决这些问题的设计模式举例 设计模式所支持的设计的可变方面 设计模式背后的6大 ...
- 好书整理系列之-设计模式:可复用面向对象软件的基础 4
第4章结构型模式 结构型模式涉及到如何组合类和对象以获得更大的结构.结构型类模式采用继承机制来 组合接口或实现.一个简单的例子是采用多重继承方法将两个以上的类组合成一个类,结果 这个类包含了所有父类的 ...
- 《设计模式:可复用面向对象软件的基础》——创建型模式(2)(笔记)
文章目录 三.创建型模式 3.4 PROTOTYPE(原型) 1.意图 补充部分 2.动机 3.适用性 4.结构 5.参与者 6.协作 7.效果 8 实现 9.代码示例 10.相关模式 3.5 SIN ...
- 《设计模式:可复用面向对象软件的基础》——行为模式(笔记)
文章目录 五.行为模式 5.1 CHAIN OF RESPONSIBILITY(职责链) 1.意图 2.动机 3.适用性 4.结构 5.参与者 6.协作 7.效果 8.实现 9.代码示例 10.相关模 ...
最新文章
- 港中文自动驾驶点云上采样方法
- POS机系统 秘钥体系
- 附带数据库的应用程序
- 2013\National _C_C++_A\1.填算式
- java 判断文件是否打开过_【后端开发】Java中如何判断文件是否被隐藏?(代码示例)...
- R 指定安装镜像的方法
- 【重识 HTML + CSS】基本 CSS 属性
- Markdown数学符号
- Mono for Android 4.2初探
- 协创物联网合肥产业园项目远程预付费电能管理系统的设计与应用
- 计算机实验小学教导主任,小学教导主任2020年度考核个人总结5篇
- Mac 全局安装 webpack 报错权限不足解决方法
- Oracle | 初级-第一章 Oracle概述
- 幸福人生在于正确选择
- ubuntu 下载 wps 及 字体缺失问题
- python爬虫代理ip_Python爬虫如何获取代理ip及ip验证?
- : ERR Client sent AUTH, but no passw redis解决
- iOS 获取网络时间
- ICPLAZA凭BFT+POS快速“出圈” 打造更繁荣的生态系统
- 软件教父马丁.福勒的心灵鸡汤
热门文章
- 20176408李俊 线性表
- 2022汽车驾驶员(高级)考试模拟100题及在线模拟考试
- CAN控制器SJA1000波特率计算方法
- 2017南京师范大学计算机学院录取名单,关于公布南京师范大学2017年硕士研究生复试成绩及录取名单的通知...
- php项目分布式架构设计,【转】互联网项目架构之基于服务的分布式架构
- 青年论坛:谈判的情感力量
- 华为机试---年终奖(动态规划)
- oracle12c amm,oracle 11gR2和12C中引入AMM和hugepage的使用
- w10计算机配置在哪看,win10如何查看电脑配置参数(一招查看电脑配置好坏)
- 机器学习Matplotlib的简单使用