第六章 抽象数据类型(ADT)

第六章 抽象数据类型(ADT)

ADT

  • 由一组操作所刻画的数据类型,而非数据的具体表示(传统类型)

  • 强调"作用于数据上的操作",程序员和 client无需关心数据如何具体存储的,只需设计/使用操作即可。

  • ADT是由操作定义的,与其内部如何实现无关!

ADT分类

可变数据类型

  • 提供了可改变其内部数据的值的操作

不可变数据类型

  • 其操作不改变内部值,而是构造新的对象

有时一个类型会以两种形式提供,一种是可变的,一种是不可变的。 例如,StringBuilder 是 String 的可变版本(但两者肯定不是同一 Java 类型,并且不可互换)。

ADT操作

构造器(Creators)

  • 以其他类型为参数创建当前类型,也就是说不能接受被构造的类型的对象。

  • 两种方式

    • 构造函数(constructor)

      • 通过new创建
    • 静态工厂方法

生产器(Producers)

  • 由旧对象创建新对象

观察器( Observers)

  • 以当前对象类型为参数,返回不同类型的对象

变值器(Mutators)

  • 更改对象属性

设计好的ADT

  • 设计简洁、一致的操作

    • 例如,我们可能不应该向 List 添加求和运算。 它可能会帮助处理整数列表的客户,但是字符串列表呢?
  • 要足以支持 client 对数据所做的所有操作需要,且 用操作满足 client 需要的难度要低

    • 例如,没有get()操作就无法获取list的内部数据
    • 例如,对于 List 来说 size 方法并不是绝对必要的,因为我们可以对递增的索引应用 get 直到失败,但这是低效且不方便的。 用遍历方式获取list的size太复杂,应该提供size()操作,方便客户端使用

表示独立性(Representation Independence)

  • client 使用 ADT 时无需考虑其内部如何实现, ADT 内部表示的变化不应影响外部 spec 和客户端。

  • 除非 ADT 的操作指明了具体的 pre-和 post-condition ,否则不能改变 ADT 的内部表示 ——spec 规定了client 和 implementer 之间的契约。

  • 不能暴露属性,属性尽量用private,不要让用户获取到属性的引用

ADT测试

  • 测试 creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec;

  • 测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确。

风险:如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失效。

在这里插入图片描述

不变性(Invariants)

  • 不变量是程序的一个属性,对于程序的每个可能的运行时状态,它始终为真。

  • immutability就是一个典型的“不变量”

  • 由ADT来负责其不变量,与client端的任何行为无关

    • 它不依赖于客户端的良好行为
    • 正确性不依赖于其他模块

为什么需要不变量

  • 保持程序的“正确性”,容易发现错误

表示泄露

  • 不仅影响不变性,也影响了表示独立性

  • 风险

    • 一旦泄露,ADT内部表示可能会在程序的任何位置发生改变(而不是限制在ADT内部),从而无法确保ADT的不变量是否能够始终保持为true。
  • 避免泄露的方法

    • 使用private和public修饰域

    • 使用final

    • 接受mutable类型作为自己属性,或者返回自己的mutable类型时

      • 复制一份信息的
      • 在规约中提出调用者不能更改(当复制代价很高时)

检查不变性

  • 调用方法后是否满足不变性
  • 是否有表示泄露

AF(Abstraction Function)和RI(Rep Invariant)

值的两个空间

  • 表示空间R rep values

    • ADT开发者关注
  • 抽象空间A abstract values

    • client看到和使用的值

R和A的关系

  • 满射

    • A中每个值都对应R中某个值或某些值
  • 未必单射

    • A中某个值在R中对应多个值(不是一对一,就是多对一)
  • 未必双射

    • R中存在值在A中没有对应的值

抽象函数 AF Abstraction Function

  • R和A之间映射关系的函数,即如何去解释R中的 每一个值为A中的每一个值。

表示不变性 RI

  • 某个具体的“表示”是否是“合法的”
  • 在对象的初始状态不变量为true,在对象发生变化时,不变量也要为true

不同的内部表示,需要设计不同的AF和RI

选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射到抽象空间中的值。

即使是同样的R、同样的RI,也可能有不同的AF,即“ 解释不同“

AF(s) 代表括号中的R域中的值映射到A域中的值

用户需要知道

  • A空间
  • ADT的四个器

判断RI

  • RI是规则,ADT从始至终都需满足

  • 区分AF和RI

checkRep

  • 好处

    • 尽早捕获错误
  • 用处

    • 在所有可能改变rep的方法内都要检查,Observer方法可以不用,但建议也要检查,以防止你的"万一"
    • 每个方法return之前,用checkRep()检查不变量是否得以保持。

有益的可变性

对immutable的ADT来说,它在A空间的abstract value应是不变的,但其内部表示的R空间中的取值则可以是变化的。这种mutation 只是改变了 R 值,并未改变 A 值,对 client 来说是immutable的“ AF 并非单射”,从一个R值变成了另一个R值, 但这并不代表在immutable的类中就可以随意出现mutator!

记录AF, RI和Safety from Rep Exposure

  • 要精确的记录RI:rep中的所有fields何为有效

  • 要精确记录AF:如何解释每一个R值

  • 表示泄漏的安全声明:给出理由,证明代码并 未对外泄露其内部表示——自证清白

ADT的规约

  • ADT的规约里只能使用client可见的内容来撰写,包括参数、返回值、异常等。

  • 如果规约里需要提及“值”,只能使用A空间中的“值”。

  • ADT的规约里也不应谈及任何内部表示的细节,以及R空间中的任何值

  • ADT的内部表示(私有属性)对外部都应严格不可见

  • 故在代码中以注释的形式写出AF和RI而不能在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏

用ADT不变量取代复杂的Precondition

  • 相当于将复杂的precondition封装到了ADT内部

  • 在这里插入图片描述
    把输入的参数,封装成一个特点的数据类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值