Levels reusable components
源代码级别的复用
– Requirements 需求
– Design and specifications 设计/规约spec
– Data 数据
– Test cases 测试用例
– Documentation 文档
白盒复用:源代码可见,可修改和扩展
复制已有代码当正在开发的系统,进行修改
可定制化程度高
黑盒复用:源代码不可见,不能修改
只能通过API接口来使用,无法修改代码
简单,清晰
适应性差些
模块级别的复用:类/抽象类/接口
Approaches of reusing a class:
inheritance继承
delegation委托
库级别的复用:API/包
系统级别的复用:框架
框架:一组具体类、抽象类、及其之间的连接关系
白盒框架,通过代码层面的继承进行框架扩展
黑盒框架,通过实现特定接口/delegation进行 框架扩展
External observations of reusability
1. Type Variation 类型可变
泛型
2. Routine Grouping 功能分组
提供完备的细粒度操作,保证功能的完整性,不同场景下复用不同的 操作(及其组合)
3. Implementation Variation 实现可变
ADT有多种不同的实现,提供不同的representations和 abstract function,但具有同样的specification (pre-condition, postcondition, invariants),从而可以适应不同的应用场景
4. Representation Independence 表示独立
内部实现可能会经常变化,但客户端不应受到影响。
表示独立性、信息隐藏
5. Factoring Out Common Behaviors 共性抽取
将共同的行为(共性)抽象出来,形成可复用实体:父类、抽象类
Behavioral subtyping/LSP原则
Compiler-enforced rules in Java (static type checking)
Subtypes can add, but not remove methods 子类型可以增加方法,但不可删
Concrete class must implement all undefined methods 子类型需要实现抽象 类型中的所有未实现方法
Overriding method must return same type or subtype 子类型中重写的方法 必须有相同或子类型的返回值或者符合co-variance的参数
Overriding method must accept the same parameter types 子类型中重写的 方法必须使用同样类型的参数或者符合contra-vshuofaariance的参数**(java不支持参数反协变,会当作重载)**
Overriding method may not throw additional exceptions 子类型中重写的方 法不能抛出额外的异常
LSP原则
前置条件不能强化
后置条件不能弱化
不变量要保持
子类型方法参数:逆变
子类型方法的返回值:协变
异常类型:协变
Covariance (协变)
返回值类型 & 异常的类型:不变或变得更具体
Contravariance (反协变、逆变)
参数类型:要相反的变化,要不变或越来越抽象**(java不支持参数反协变,会当作重载)**
应用中的例子
**console :car **
Generics 泛型
LSP与泛型
是的子类
不是的子类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kw6bTY2J-1596861105052)(https://i.loli.net/2020/06/13/SauAgL2Vv3bilWh.png)]
通配符
可以接受 : , , and
可以接受:和number的子类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpoC8CVP-1596861105059)(https://i.loli.net/2020/06/13/DjAfa2lzXCT64Sm.png)]
Delegation and Composition
Composite Reuse Principle (CRP)
在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。
继承我们叫做白箱复用,相当于把所有的实现细节暴露给子类。
组合/聚合也称之为黑箱复用,对类以外的对象是无法获取到实现细节的。
Types of delegation
Dependency:临时性的delegation
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r2l7iKU3-1596861105060)(https://i.loli.net/2020/06/13/RAb327WZJUo4Ffz.png)]
Association: 永久性的delegation
Composition(组合):更强的association,但难以变化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4wxmSZA-1596861105063)(https://i.loli.net/2020/06/13/xTW2l71ImZd5tMB.png)]
Aggregation(聚合): 更弱的association,可动态变化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMQ8t7eD-1596861105065)(https://i.loli.net/2020/06/13/q7lkO2r94ztxb1F.png)]
Design Patterns for Reuse
Structural patterns
Adapter
将某个类/接口转换为client期望的其他形式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Gw1Vtmz-1596861105067)(https://i.loli.net/2020/06/18/5H7vGZbglQWieuK.png)]
Decorator
当需要特性的任意组合的时候,可以用装饰器模式
subtyping + delegation
抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类
装饰角色(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口
具体装饰角色(ConcreteDecorator):负责给构件对象“贴上”附加的责任
重要的一点补充:
装饰模式对客户端的透明性要求程序不要声明一个ConcreteComponent类型的变量,而应当声明一个Component类型的变量。
用上面的例子来说,必须永远把所有的饮料当成饮料来对待,而如果把饮料变成的加摩卡的饮料当成摩卡,而不是饮料,这是不应当发生的。
Adapter和Decorator的关系
装饰模式和适配器模式都是“包装模式(Wrapper Pattern)”,它们都是通过封装其他对象达到设计的目的的。
区别:
理想的装饰模式在对被装饰对象进行功能增强的同时,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。
适配器模式并不要求对源对象的功能进行增强,但是会改变源对象的接口,以便和目标接口相符合。
拓展:
并且,装饰模式有透明和半透明两种,这两种的区别就在于装饰角色的接口与抽象构件角色的接口是否完全一致。
透明的装饰模式也就是理想的装饰模式。半透明的装饰器模式如下图:
Template
共性的步骤在抽象类内公共实现,差 异化的步骤在各个子类中实现
inheritance + overridable

Iterator
对放入集合类里的ADT提供统一的遍历方法
Iterator 迭代器接口
ConcreateIterator 具体实现类
Aggregate 集合类接口
ConcreteAggregate 具体的集合类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRxSAfWV-1596861105074)(https://i.loli.net/2020/06/18/kXBmFbxuvzgCZRJ.png)]
好处:
一个重要的原因,引入 Iterator 后可以将遍历与实现分离出来。
不论具体集合类如何实现(数组or链表),下面的遍历代码都不用改动
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!