HIT 软件构造期末复习六 抽象数据类型


介绍
抽象数据类型与表示独立性:如何设计良好的抽象数据结构,通过封装来避免客户端获取数据的内部表示(即“表示泄露”),避免潜在的bug——在client和implementer之间建立“防火墙”

ADT的特性:表示泄漏、抽象函数AF、表示不变量RI
基于数学的形式对ADT的这些核心特征进行描述并应用于设计中。

1.抽象和定义类型

编程语言本身带有一些数据类型,如:integers, booleans, strings, 等。用户可以使用他们作为输入和输出

用户也可以定义自己需要的数据类型并且使用他们。

传统的类型定义:关注数据的具体表示,例如::创建一个整形字段来记录年月日

抽象类型: 抽象类型:强调“作用于数据上的操作”,程序员和
client无需关心数据如何具体存储的,只需设计/使用操作即可。例如:一个布尔值的操作有:与或非;我们不关心用什么值表示真假,只关心定义在类型上的操作。
例如:但我们谈论List类型时,我们不关心它是一个链表还是数组又或者是其它的表示list的特殊数据结构,我们关心的是定义在List上的一系列操作,如get(),size()等

2.对操作和类型的分类

**可变数据类型:**提供了可改变其内部数据的值得操作
不可变数据类型:其操作不改变内部值,而是构造新的对象

对数据的操作有四类:
Creators:构造器,从无到有创建一个新的对象
Producers:生产器,从一个之前的对象生产一个新的对象
Observers:观察器,获取对象的属性
Mutators:变值器,改变对象属性

例子:
在这里插入图片描述
构造器要么是一个构造函数,或者是一个静态方法
在这里插入图片描述

变值器常常没有返回,如果返回值为void,则必然意味着它改变了对象的某些内部状态,变值器也可能返回非空类型。

不可变的抽象数据类型没有变值器

3.抽象数据类型的案列

intString在这里插入图片描述

List是可变的,也是一个接口,它意味着其他类提供实际执行 数据类型,如ArrayList和 LinkedList。
在这里插入图片描述
在这里插入图片描述

4.设计一个抽象类

设计好的ADT:靠“经验法则”,提供一组操作,设计其行为规约 spec;
法则一:操作简单不复杂,每一个操作都有清晰的目的,行为连贯。
法则二:要足以支持客户端对数据所做的所有操作,且操作满足客户端的需求的难度要低。
法则三:要么抽象,要么具体,不要混合,即:要么针对抽象设计,要么针对具体应用设计。

5.表示独立性

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

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

客户端只需看spec和函数名即可知道该操作是什么,不需要看实现的代码:
在这里插入图片描述
上述是各类操作,下面实现
在这里插入图片描述
在这里插入图片描述

因为这个数据类型是不可变的,所以子字符串操作 不需要将字符复制到一个新的数组中。 它可以指向原始的MyString对象的字符数组 并记录新子字符串对象的开始和结束 代表。 为了实现这个优化,我们可以改变内部 这个类的表示为:
在这里插入图片描述

6.测试抽象数据类型

需要对每一个方法都建立一个合适的测试
测试creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec;
**测试observers:**调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确。

**风险:**如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失效。
在这里插入图片描述

7.不变量

好的抽象数据类型最重要的属性是它保留了自己的不变量。

不变量是程序的一种属性,在程序运行时的每一个这阶段都保持为真

不可变性是一个关键的不变量:一旦创建,就是一个不可变对象
应该在整个生命周期中始终表示相同的值。 例如: 不变性

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

为什么需要不变量:保持程序的“正确性”,容易发现错误

在具有不变性的地方,一旦某个方法所使用的,就要在使用后检查是否改变,总是要假设client有“恶意”破坏ADT的不变量-

在这里插入图片描述
对不变性的第一个威胁来自于客户可以直接访问它的字段。
在这里插入图片描述
上面是一个表示泄露的案例,意味着类外部的代码能够直接修改
不仅影响不变性,也影响了表示独立性:无法在不影响客户端的情况下改变其内部表示

public:class的外部也可以访问
private:只有类的内部可以访问

final:不可变的数据类型在创建后不会被冲洗赋值构造

在这里插入图片描述
在这里插入图片描述
这个方法接受了一个tweet ,并在一小时后转发它
d.timestamp 和 t.timestamp 指向t的timestamp,是同一个对象的时间,之后d做了修改,
在这里插入图片描述
由于timestamp定义了fianl,Tweet的不可变性被破坏
同时也暴露了内部数据

解决方案:防御是拷贝
在这里插入图片描述
可变类型通常有一个复制构造函数允许您这样做
通过复制现有的对象创建一个相同的对象

在这里插入图片描述
24个指向的都是同一个date,解决方案:防御是拷贝
在这里插入图片描述
如果任何类型是可变的,请确保您的实现不返回对其表示形式的直接引用。

除非迫不得已,否则不要把希望寄托于客户端上,ADT有责任保证自己的invariants,并避免“表示泄露”。

最好的办法就是使用immutable的类型,彻底避免表示泄露

8.表示不变性和抽象函数

一般情况下ADT的表示比较简单,有些时候需要复杂表示
R:表示值的空间 (rep值)由实际实现的实体构成。
A抽象值构成的空间:client看到和使用的值

在这里插入图片描述

ADT开发者关注表示空间R,client关注抽象空间A
例如:
使用一个字符串去表示一个字符集和
在这里插入图片描述
表示空间由字符串构成,而抽象空间则由字符集合构成

映射规则:
满射:每个抽象值都要代表值映射到他
未必单射:每个抽象值可以有多个代表制映射到他
未必双射:存在代表值没有映射,没有映射代表表示值不合法

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

在这里插入图片描述
例如:一个图映射到布尔值的表示不变性
RI : R → boolean
对于一个rep值r,当且仅当r被AF映射时,RI®为真。
换句话说,RI告诉我们给定的代表值是否为真形式。
或者,您可以将RI看作一个集合:它是rep的子集 值上定义了AF

表示不变性RI:某个具体的“表示”是否是“合法的”
也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值
也可将RI看作:一个条件,描述了什么是“合法”的表示值

在这里插入图片描述
不同的内部表示,需要设计不同的AF和RI

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

即使是同样的R、同样的RI,也,可能有不同的AF,即“解释不同”。
在这里插入图片描述

设计ADT:
(1) 选择R和A;
(2) RI — 合法的表示值;
(3) 如何解释合法的表示值 —映射AF

做出具体的解释:每个rep value如何映射到abstract value,而且要把这种选择和解释明确写到代码当中

表示不变量仅仅是一个数学概念,在每次操作之后检查表示不变量可以快速发现bug

在所有可能改变rep的方法内都要检查
Observer方法可以不用,但
建议也要检查,以防止你的“万一”

在这里插入图片描述

9.有益的突变

抽象的值永远不应该改变。
但是实现可以自由地改变代表值。
只要它是继续映射到相同的抽象值,以便更改对客户端不可见。
这种变化被称为有益突变

对immutable的ADT来说,它在A空间的abstract value应是不变的。
但其内部表示的R空间中的取值则可以是变化的。

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

在这里插入图片描述

10.记录AF、RI和 代表暴露的安全性

要精确的记录RI:rep中的所有fields何为有效
要精确记录AF:如何解释每一个R值
要精确记录AF:如何解释每一个R值
给出理由,证明代码并未对外泄露其内部表示——自证清白

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
ADT的规约里只能使用client可见的内容来撰写,包括参数、返
回值、异常等。
如果规约里需要提及“值”,只能使用A空间中的“值”
ADT的规约里也不应谈及任何内部表示的细节,以及R空间中的任何值
ADT的内部表示(私有属性)对外部都应严格不可见

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

在这里插入图片描述
表示泄漏的风险:一旦泄露,ADT内部表示可能会在程序的任何位置
发生改变(而不是限制在ADT内部),从而无法确保ADT的不变量是
否能够始终保持为true。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值