第九章 面向复用的软件构造技术

第九章 面向复用的软件构造技术

什么是软件复用

  • 软件重用是使用现有软件组件实现或更新软件系统的过程。

  • 软件复用的两个方面

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

特点

  • 很大的适应性

  • 降低成本和开发时间

  • 充分的测试->高可靠

  • 标准化、一致化

    • GUI 库的重用会在应用程序中产生一致的外观。
  • 针对性不强->性能差

  • 往往无法拿来就用,需要适配

  • 可靠性建立在外部基础上

  • 面向复用编程代价高

    • 可重用的组件应该以明确定义的、开放的方式设计和构建,具有简洁的界面规范、易于理解的文档,并着眼于未来的使用。
  • 基于复用编程的前期代价也高

    • 它涉及跨组织、技术和流程的变更,以及支持这些变更的工具成本,以及培训人员使用新工具和变更的成本。
    • 在这里插入图片描述

如何使用已有软件进行开发

  • 在这里插入图片描述

  • 建立可复用软件库,对其进行有效管理

  • 选择需要的软件

  • 进行适配

    • 可能必须向组件添加额外的功能。 添加后,新组件可以重新使用
    • 可以从组件中删除不需要的功能以提高其性能或减少其空间要求
    • 可能必须修改某些组件操作的实现

怎样评测可复用性

  • 复用的场合有多少

  • 复用的机会有多频繁

  • 复用的代价有多大

    • 搜索、获取
    • 适配、扩展
    • 实例化
    • 与软件其他部分的互连的难度

好的可复用性软件应该

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

可复用代码来源

  • – Internal (corporate) code libraries 组织的内部代码库(Guava)
  • – Third party libraries 第三方提供的库(Apache)
  • – Built-in language libraries 语言自身提供的库(JDK)
  • – Code samples from tutorials, examples, books, etc. 代码示例
  • – Local code guru or knowledgeable colleague 来自同事
  • – Existing system code 已有系统内的代码
  • – Open source products (be sure to follow any licensing agree 开源软件的代码

复用的级别和形态

复用的级别

  • 源代码(方法和具体语句)

    • 复制粘贴到自己的程序

    • 存在维护问题

      • 需要在多个地方更正代码
      • 有太多的代码需要处理(很多版本)
    • 查找需要代码的网站

      • grepcode.com
      • github.com/search
      • searchcode.com
  • 模块(类和接口)

    • 类是复用的最小单元

      • 源代码不重要,可以直接打包成jar包
      • 文档很重要
      • 封装有助于重用
      • 更少的代码需要管理
      • 版本管理、向后兼容仍是问题
      • 需要将相关的类打包在一起 – 静态链接
    • 两种复用方式

      • 继承

        • 要有严格的父子关系
        • 不能取消属性或方法
        • 需要仔细设计继承层次
      • 委托

        • 当一个对象依靠另一个对象来实现它的某些子集的功能时(一个实体将某些东西传递给另一个实体),委托是简单的。

        • 委托可以被描述为实体之间共享代码和数据的低级机制。

        • 显式和隐式

          • 显式委托

            • A use B
            • 类A和B之间没有什么关联,当A中某个功能需要借助B实现时,将方法参数设置为B类型,通过传入B的对象,调用B的方法来完成。
            • 在这里插入图片描述
          • 隐式委托

            • A has B

            • 在类A中维护着一个类B的对象作为A的成员变量,从而可以调用B的方法,实现复用。

            • 类型

              • 组合

                • 不对外提供改变B类型的成员变量的方法,B类型的成员变量伴随A类型对象创建而创建,A类型对象消失而消失,具有相同的生命周期。
                • 在这里插入图片描述
              • 聚合

                • 在A类型对象创建时并不为B类型对象进行赋值,可随时由外部传入进行赋值,需要调用B中方法时再调用
                • 在这里插入图片描述
              • 联系

                • 先和组合类似,在初始新建B的对象,但同时也像聚合一样提供变值函数
                • 在这里插入图片描述
  • 库(API)

    • 一组提供可重用功能的类和方法 (API)
  • 架构层面(框架)

    • 一组具体类、抽象类、及其之间的连接关系

    • 开发者根据 framework的规约,填充自己的代码进去,形成完整系统

    • 将framework看作是更大规模的API复用,除了提供可复用的API,还将这些模块之间的关系都确定下来,形成了整体应用的领域复用

    • 在这里插入图片描述

    • 框架不同于应用程序

      • 抽象程度不同,因为框架为一系列相关问题提供了解决方案,而不是单一问题
      • 为了适应一系列的问题,框架是不完整的,允许定制。
    • 与库的区别:调用关系不同

      • 开发者构造可运行软件实体,其中涉及到对可复用库的调用
      • Framework作为主程序加以执行,执行过程中调用开发者所写的程序
    • 按照扩展的技术划分

      • 黑盒框架

        • 通过实现特定接口/委派进行框架扩展

        • 通过为可插入框架的组件定义接口来实现可扩展性

        • 通过定义符合特定接口的组件来重用现有功能

        • 这些组件通过委托与框架集成

        • 特点

          • – 允许扩展暴露在接口中的功能
          • – 只需要了解接口
          • – 多个插件
          • – 通常提供更多的模块化
          • – 可以单独部署(.jar、.dll、…)
          • – 通常是所谓的最终用户框架、平台
        • 在这里插入图片描述

      • 白盒框架

        • 通过代码层面的继承进行框架扩展

        • 通过继承和动态绑定实现可扩展性

        • 特点

          • – 允许扩展每个非私有方法
          • – 需要理解超类的实现
          • – 一次只能扩展一个
          • – 一起编译
          • – 通常是所谓的开发者框架
        • 在这里插入图片描述

复用的形态

  • 白盒复用

    • 源代码可见,可修改和扩展

    • 复制已有代码到正在开发的系统,进行修改

    • 优点

      • 可定制化程度高
    • 缺点

      • 对其修改增加了软件的复杂度,且需要对其内部充分的了解
  • 黑盒复用

    • 源代码不可见,不能修改

    • 只能通过API接口来使用,无法修改代码

    • 优点

      • 简单,清晰
    • 缺点

      • 适应性差些

设计可复用的类

行为子类型和LSP原则

  • 子类型多态

    • 客户端可用统一的方式处理不同类型的对象
  • 行为子类型

    • Java 中的编译器强制规则(静态类型检查)

      • 子类型可以增加方法,但不可删除方法
      • 子类型需要实现抽象类型中的所有未实现方法
      • 子类型必须返回相同类型或者子类型(协变)
      • 子类型必须接受相同类型(Java中不支持逆变)
      • 子类型中重写的方法不能抛出额外的异常
    • 也适用于指定的方法

      • 更强的不变量

        • 在这里插入图片描述
          不要改父类RI(否则可能行为不一致),可以增加自己的RI,增加自己的RI后要保证父类中没有方法破坏自己的RI,如果有要重写
      • 更弱的前置条件

        • 在这里插入图片描述
      • 更强的后置条件

    • LSP原则(强行为子类型化)

      • 前置条件不能强化
      • 后置条件不能弱化
      • 不变量要保持
      • 子类型方法参数:逆变
      • 子类型方法的返回值:协变
      • 异常类型:协变
    • 协变和逆变

      • 协变

        • 父类型->子类型:越来越具体specific
      • 逆变

        • 子类型->夫类型:越来越抽象abstract
      • 在这里插入图片描述
        会调用valueOf(),无法接收

    • 泛型中的LSP

      • 泛型的继承关系

        • 在这里插入图片描述

        • 在这里插入图片描述
          只有尖括号中存在包含关系时才是父子关系(静态检测)

        • 在这里插入图片描述

      • 类型擦除

        • 类型参数的类型信息在代码编译完成后被编译器丢弃,因此这些类型信息在运行时是不可用的。
        • 在通用类型中用它们的边界或Object(如果类型参数没有边界)替换所有类型参数。因此,产生的字节码只包含普通的类、接口和方法
        • 在这里插入图片描述

委派

  • 实现排序的两种方法

    • 在这里插入图片描述
      实现Comparator接口并override compare()函数
    • 在这里插入图片描述
      让ADT 实现 Comparable 接口,然后 override compareTo() 方法。与使用Comparator的区别:不需要构建新的Comparator类,比较代
      码放在ADT内部。
  • 一个类不需要继承另一个类的全部方法,通过委托机制调用部分方法,从而避免大量无用的方法

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

  • 在这里插入图片描述

  • 在这里插入图片描述

  • 在这里插入图片描述

  • 委派的三种类型

    • Use(A use B)

      • 临时性的delegation
      • 在这里插入图片描述
    • Association联系 (A has B)

      • 永久性的delegation
      • 子主题 2
    • Composition/aggregation (A owns B)

      • Composition/Aggregation是Association的两种具体形态

      • Composition组合

        • 更强的association,但难以变化
        • 在这里插入图片描述
      • Aggregation聚合

        • 更弱的association,可动态变化
        • 在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值