HIT-SC-Chapter Six

HIT-SC-Chapter Six

Abstract Data Type (ADT)

1 Abstract Data Type (ADT)

  • User-Defined Types
    • 除了编程语言自带的基本数据类型和对象数据类型
  • Data Abstraction
    • 由一组操作所刻画的数据类型
      • number 可以add and multiply
      • string 可以 concatenate and substrings
    • 而传统的类型定义:关注数据的具体实现
    • 程序员无需关心数据如何具体存储,只需设计/使用操作即可
    • image-20220609101846962
  • An abstract type is defined by its operations
    • 类型T的操作集及其说明完全说明了我们所说的T。
    • ADT是由操作定义的,与其内部如何实现无关

2 Classifying Types and Operations

(1) Mutable and immutable types

  • mutable:
    • 提供了可改变其内部数据的值的操作
      • 日期,因为您可以调用setMonth()并通过getMonth()操作观察更改。
  • immutable:
    • 其操作不改变内部值,而是构造新的对象
    • 有时会以可变和不可变的形式提供类型。例如,StringBuilder是String的可变版本(但两者肯定不是同一种Java类型,而且不可互换)。

(2) Classifying the operations

  • creators 构造器

    • create new objects of the type.
    • 创建者可以接受一个对象作为参数,但不能接受正在构造的类型的对象。
  • producer 生产器

    • create new objects from old objects of the type.
    • String的concat()方法是一个生成器:它接受两个字符串,并生成一个表示它们连接的新字符串。
  • Observers 观察器

    • take objects of the abstract type and return objects of a different type.
    • The size() method of List , for example, returns an int .
  • Mutators 变值器

    • 改变对象属性的方法
    • The add() method of List , for example, mutates a list by adding an element to the end.
  • image-20220609102530520

(3)操作的明显特征

  • image-20220609102649082
  • Signature of a creator:
    • 构造函数/静态函数
    • 作为静态方法实现的创建者通常称为factory method 工厂方法
  • Signature of a mutator:
    • 通常返回void
      • 返回void,则必然改变了对象的某些内部状态
    • 也可能返回非空类型
      • 如,set .add()返回一个布尔值,指示是否实际更改了集合
      • 在Java的图形用户界面工具包中,Component.add()返回对象本身,因此可以将多个add()调用链接在一起。
    • immutable has no mutators

3 Abstract Data Type Examples

  • int and String
  • image-20220609103801069
  • List
    • image-20220609103832959
  • image-20220609103842644

4 Designing an Abstract Type

  • 一组操作+行为规约spec
  • 经验法则
    • 设计简洁、一致的操作
      • 每个操作都应该有一个明确的目的,并且应该有一个连贯的行为,而不是一大堆特殊情况。
      • 例如,我们可能不应该向List添加求和操作。它可能会帮助处理整数列表的客户端,但是字符串列表呢?或者嵌套列表?所有这些特殊的情况会使求和成为一个难以理解和使用的操作。
      • 通用性
    • 足以支持client的所有需求,难度要低
      • 操作集应该是足够的,因为必须有足够的操作来完成客户机可能想要执行的计算类型。
      • 一个好的测试是检查该类型对象的每个属性都能被提取出来。
        • 例如,如果没有get操作,我们将无法找出列表的元素是什么。
      • 基本信息不应难以获得
        • 例如,size方法对于List来说并不是严格必要的,因为我们可以对递增的索引应用get,直到我们得到一个失败,但是这样做效率低且不方便。
    • 要么抽象、要么具体,不要混合---要么针对抽象设计,要么针对具体应用的设计
      • 相反,将特定于领域的方法(如dealCards)放入泛型类型List中是没有意义的。

5 Representation Independence

  • 内部成员变量和方法都发生了变化,但是Client的调用方法是一致的。也就是说:内部变化不影响外部的调用。是独立的。
  • image-20220609105129589
  • image-20220609105116160
  • image-20220609105213944

6 Testing an Abstract Data Type

  • 我们通过为ADT的每个操作创建测试来为其构建测试套件。
  • 这些测试不可避免地相互影响。
  • 测试创建器、生成器和变异器的唯一方法是在产生结果的对象上调用观察器,同样,测试观察器的唯一方法是创建它们要观察的对象。
    • 测试creators, producers, and mutators:调用observers来观察这些 operations的结果是否满足spec;
    • 测试observers:调用creators, producers, and mutators等方法产生或 改变对象,来看结果是否正确。
  • 划分ADT操作的输入空间
    • image-20220609105417265
  • 覆盖所有分区的测试套件
    • image-20220609105629843

7 Invariants

  • ADT的不变量
  • 好的抽象数据类型最重要的属性是它保留自己的不变量
    • 在任何时候都是true
  • 由ADT负责其不变量,与client端的任何行为无关
    • 当一个ADT能够确保它内部的不变量恒定不变(不受使用者/外部影响),我们就说这个ADT保护/保留自己的不变量.
  • 为什么需要不变量?
    • why
  • 总是假设client有意破坏ADT的不变量,防御性编程
  • Immutability as a type of Invariants

(1)exposure

  • image-20220609110250050
  • 直接访问它的字段
    • 不仅影响不变性,也影响独立性
      • 我们无法在不影响所有直接访问这些字段的客户端情况下更改Tweet的实现。
  • private和public关键字
    • 指出哪些字段和方法只能在类内部访问,哪些字段和方法可以从类外部访问。
  • final关键字
    • 有助于确保这个不可变类型的字段在对象构造后不会被重新赋值。

  • 直接访问引用

  • 防御性复制

    • 假定用户去毁坏不变性(可能是有意的,也可能是无意的)
    • 确保类不变量在任何输入下都有效,以最小化可变性
    • image-20220609111234477
  • Copy() and Clone()

  • 如果任何类型是可变的,请确保您的实现不会返回对其表示形式的直接引用


  • 转移矛盾

    • 当复制代价很高时
    • 但引发的潜在bug也很多
  • 最好的方法是使用immutable的类型,彻底避免表示泄露

  • image-20220609111552813


SUM

image-20220609111827936


8 Rep Invariant and Abstraction Function

  • ADT的表示空间和抽象空间
    • 表示空间:ADT的表示
    • 抽象空间:client看到和使用的值
  • image-20220609112148641
  • 抽象类型的实现者必须对表示值感兴趣,因为实现者的工作是使用表示值空间来实现抽象值空间的假象。
  • Mapping between two spaces
    • 满射,未必单射,未必双射
    • 一些抽象值被多个代表值映射
    • 每个抽象值都被一些代表值映射
    • 并不是所有代表值都被映射
  • image-20220609112544985

(1) AF

抽象函数

  • R和A之间映射关系的函数,即如何去解释R种的每一个值为A中的每一个值
  • 图中的弧线显示了抽象函数
  • image-20220609112806058

(2) RI :another important ADT invariants

  • A rep invariant that maps rep values to booleans:
    • RI : R → boolean
  • For a rep value r , RI(r) is true if and only if r is mapped by AF .
  • 换句话说,RI告诉我们给定的代表值是否格式良好。
  • image-20220609125526790

(3) Documenting RI and AF

  • 不变量和抽象函数都应该记录在代码中,就在rep本身声明的旁边

    • image-20220609125757440
  • A集合单独无法决定AFRI

  • 同一抽象类型可以有几种表示形式。

    • 一组字符可以同样地表示为一个字符串(如上所示),也可以表示为一个位向量(每个位对应一个可能的字符)。
      • 显然,我们需要两个不同的抽象函数来映射这两个不同的代表值空间。
    • 不同的内部表示,需要设计不同的AF和RI
    • 先确定R,进而指定RI,再利用AF解释。
    • image-20220609130302165
  • 相同的表示空间RI亦会不同

    • 例如,如果我们允许字符串中存在重复项,或要求对字符进行排序,以非减量顺序出现,那么将有相同的rep值空间,但不同的rep不变量。
    • image-20220609130418041
  • 同样的R和RI,也可能会有不同的AF,即解释不同

    • 也许我们将连续的字符对解释为子区域,这样字符串代表“acgg”被解释为两个范围对[a-c]和[g-g],因此代表集合{a,b,c,g}
    • image-20220609135406755
  • How RI and AF influence ADT design

    • 设计ADT
      • 选择R和A
      • 选择RI--合法的表示值
      • 如何解释合法的表示值--映射AF
        • 做出具体的解释:每个rep value如何映射到abstract value
        • 而且要把这种选择和解释明确写到代码当中
  • image-20220609151150909

    • image-20220609151216451

    • image-20220609151257633

(4) Checking the Rep Invariant 随时检查RI是否满足

  • 在所有改变rep的方法内都要检查,调用checkRep()
    • Observer方法可以不用,但建议也要检查
    • 在每个方法(包括观察器)中执行checkRep()意味着您将更有可能捕获由再现暴露引起的再现不变量违反
  • image-20220609151716712

9 Beneficent mutation

  • 回想一下,当且仅当类型的值在创建后从未更改时,类型才是不可变的。

  • 通过对抽象空间A和代表空间R的新理解,我们可以完善这个定义:抽象值永远不应该改变。(A)

    • With our new understanding of the abstract space A and rep space R, we can refine this definition: the A 从不改变
  • 但是,只要rep值继续映射到相同的抽象值,实现就可以自由地修改rep值,这样客户机就看不到更改。

  • This kind of change is called beneficent mutation (有益的可变性).

  • image-20220609152033431

  • image-20220609152105362

  • image-20220609152113870

    • 除以公因子
    • 分子分母同时除以相同的公因数,或者同时乘以-1,对抽象函数的结果没有影响
    • rep变了
    • 另一种思考方式是:AF是一个多对一函数,代表值改变为另一个仍然映射到相同的抽象值。
  • image-20220609152406003

  • 这种实现者自由通常允许诸如此类的性能改进

    • image-20220609152438634

    • 通过牺牲immutability的部分原则来换取“效率”和“性能”


10 Documenting the AF, RI, and Safety from Rep Exposure

(1) Documenting AF and RI

  • 在代码中用注释形式记录AF和RI
    • 不能简单地写为rep中所有的fields为有效
    • RI的工作是精确解释字段值如何有效
  • 精确地记录AF:如何解释每一个R值
    • 抽象函数的工作是精确定义具体字段值的解释方式。
    • 作为一种函数,如果我们将记录的AF替换为实际(合法)字段值,我们应该得到它们所代表的单个抽象值的完整描述。
      • As a function, if we take the documented AF and substitute in actual (legal)
        field values, we should obtain out a complete description of the single
        abstract value they represent.
      • 作为一种功能,如果我们将记录的AF替换为实际(合法)字段值,我们应该得到它们所代表的单个抽象值的完整描述。

(2) Documenting rep exposure safety argument

  • Another piece of documentation is a rep exposure safety argument
    • 表示泄露的安全说明
  • 这是一个注释,它检查了代表的每个部分,查看了处理这部分代表的代码(特别是关于客户端的参数和返回值,因为这是代表暴露发生的地方),并给出了代码不暴露代表的原因。
    • 给出理由,证明代码并未对外泄露其内部表示
  • image-20220609154456172
  • image-20220609154506639
  • image-20220609154517371

Summary: What an ADT spec may talk about

  • ADT的spec里面只能用client可见的内容、返回值和异常
    • This includes parameters, return values, and exceptions thrown by its operations.
    • Whenever the spec needs to refer to a value of type T, it should describe the value as an abstract value, i.e. mathematical values in the abstract space A.
  • image-20220609162427015
  • 只能用A空间内的值,不能谈论R空间中的任何值和代码内部的细节
  • ADT的私有属性,对外严格不可见
    • 在代码中以注释的形式写出AF和RI,而不能在Javadoc文档中,防止被外部看到而破坏独立性/信息隐藏。
      • 将它们编写为Javadoc注释将作为类型规范的公共部分提交给它们,这将干扰代表独立性和信息隐藏。

如何建立不变量

  • 不变量是一个对整个程序都成立的属性——在对象的不变量的情况下,它减少到对象的整个生命周期

  • 对象的初始状态不变量为true,在对象发生变化时,不变量也要为true。

  • Translating this in terms of the types of ADT operations:

    • 构造器和生产器在创建对象时要确保不变量为true
    • 变值器和观察器在执行时必须保持不变性
    • 在每个方法return之前,用checkRep()检查不变量是否得以保持。
  • 表示泄露的风险:

    • 如果rep被暴露,那么对象可能在程序的任何地方被更改,而不仅仅是在ADT的操作中,而且我们不能保证在这些任意更改之后不变式仍然保持不变。
  • So the full rule for proving invariants

  • image-20220609163434512

  • image-20220609163452093


11 ADT invariants replace preconditions

用ADT不变量取代复杂的 Precondition,相当于将复杂的precondition封装到了ADT内部

  • image-20220609163608243

  • It’s safer from bugs

  • It’s easier to understand

  • It’s more ready for change

  • image-20220609163719215


SUMMARY

  • image-20220609164021613
  • image-20220609164030187
  • image-20220609164039864
  • image-20220609164047494
posted @   三金同志  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示