9.面向复用的软件构造技术

本文介绍了面向复用的编程思想,包括编程for/with复用、Liskov替代原则(LSP)、协变和反协变的概念,以及在Java中数组和泛型的子类型化。此外,还讨论了委托、Comparator和Comparable接口的使用,以及复合重用原则(CRP)。文章通过接口组合和白盒/黑盒框架的原理展示了如何实现软件的复用和扩展。
摘要由CSDN通过智能技术生成

面向复用的软件构造技术

1 Programing for/with reuse

  • programming for reuse 面向复用编程:
    开发出可复用的软件
  • programming with reuse 基于复用编程:
    利用已有的可复用软件搭建应用系统
    在这里插入图片描述

2 LSP

  • 行为子类型与Liskov替代原则(LSP)
    • 子类型可以增加方法,但不可删除父类中的方法
    • 子类型需要实现抽象类型(接口、抽象类)中所有未实现的方法
    • 子类型中重写的方法必须有相同或子类型的返回值或者符合co-variant的参数
    • 子类型中重写的方法必须使用同样类型的参数或者符合contra-variant的参数(此种情况Java目前按照重载overload处理)
    • 子类型中重写的方法不能抛出额外的异常
  • 行为子类型也适用于以下特殊方法:
    • 更强的不变量
    • 更弱的前置条件
    • 更强的后置条件
  • 在编程语言中,LSP依赖于以下限制:
    • 前置条件不能强化
    • 后置条件不能弱化
    • 保持不变量
    • 子类型方法参数:逆变
    • 子类型方法的返回值:协变
    • 异常类型:协变

3 协变、反协变

  • 父类型→子类型,返回值、异常协变(更具体)
    更具体的类可能有更具体的返回类型,这称为子类型中返回类型的协变
  • 父类型→子类型,参数逆变(更宽松)
    子类型参数的逆变在Java中被当做overload处理
    在这里插入图片描述

4 数组的子类型化

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

在这里插入图片描述

5 泛型的子类型化

  • 类型擦除
    泛型信息只存在于编译阶段,在运行时类型参数在编译后被丢弃,运行时不可用。
  • 无论何时定义泛型类型,都会自动提供相应的原始类型。原始类型的名称只是泛型类型的名称,类型参数已删除;擦除时类型变量会被擦除,替换为限定类型,如果没有限定类型则替换为Object类型。
    在这里插入图片描述

6 Delegation

  • 委派/委托:一个对象请求另一个对象的功能
  • 实现:在B类中创建A类的成员变量,调用A类的方法。
  • 很多设计模式将继承和委托结合使用。
  • 如果子类只需要复用父类的小部分方法,可以考虑委托机制实现,不需要继承父类所有方法,避免继承大量无用的方法
  • 委派模式:通过运行时动态绑定,实现对其他类中代码的动态复用
    在这里插入图片描述

在这里插入图片描述

7 Comparator和Comparable

比较器:int compare(T a,T b):比较其两个参数的顺序。

  • Comparator

    • 如果你的ADT需要比较大小,或者要放入Collections或Arrays中进行排序,可实现Comparator接口并override compare()函数。这使用了委托的思想。
      在这里插入图片描述
  • Comparable

    • 让你的ADT实现Comparable接口,然后override compareTo() 方法。
    • 与使用Comparator的区别:不需要构建新的Comparator类,比较代码放在ADT内部。这不是委托。
      在这里插入图片描述

8 CRP原则

  • 复合重用原则CRP
    • CRP原则更倾向于使用委派而不是继承来实现复用。

    • “委托”发生在object层面,而“继承”发生在class层面

    • 接口之间通过extends实现行为扩展,类实现组合接口,避免复杂的继承关系。

9 接口的组合

  • 定义组合接口interface,扩展extends两个以上的接口interface
interface Flyable {//定义抽象行为的接口
	public void 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();
	}
}

*10 委派的类型(都支持一对多):

  • Dependency,临时性的委派,用完即释放

    • Delegation关系通过方法的参数传递建立起来
      在这里插入图片描述
  • Association,永久性的委派,一直占着

    • Delegation关系通过固有的field(成员变量)建立起来
      在这里插入图片描述
  • Composition,更强的association,但难以变化

    • Delegation关系通过类内部field(成员变量初始化)建立起来,无法修改
      在这里插入图片描述
  • Aggregation, 更弱的association,可动态变化

    • Delegation关系通过客户端调用构造函数或专门方法建立起来
      在这里插入图片描述

11 白盒框架的原理与实现

  • 白盒框架,通过代码层面的继承进行框架扩展
  • 通过子类重写方法进行扩展(继承)
  • 常见的设计模式:模板方法
  • 子类拥有main方法,但可以控制框架

12 黑盒框架的原理与实现

  • 黑盒框架,通过实现特定接口/delegation进行
    框架扩展
  • 通过实现插件接口进行扩展(委托)
  • 通用设计模式:策略、观察者模式
  • 通过插件加载机制加载插件,并为框架提供控制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SolemnJudgment

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值