软件构造期末复习知识点总结

抽象数据类型(ADT)

一、抽象类型和用户定义的类型
编程语言附带内置类型(如整数、布尔符、字符串等)。以及内置的过程,例如,对于输入和输出。用户可以定义他们自己的数据类型和过程—用户定义的类型。
数据抽象:类型具有可以对其执行的操作。传统上,程序员在早期编程语言中定义自己的类型,例如:创建一个记录类型日期,使用日期、月份和年份的整数字段。抽象类型关注操作——该类型的用户不需要担心它的值是如何存储的,程序员可以忽略编译器实际存储整数的方式。最重要的就是操作。

二、对类型和操作进行分类
类型,无论是内置的还是用户定义的,都可分类为可变的或不可变的,可更改可变类型的对象:即,它们提供的操作在执行这些操作会导致同一对象上的其他操作的结果产生不同的结果。日期是可变的,因为您可以调用setMonth()并使用getMonth()操作观察更改。但是字符串是不可变的,因为它的操作会创建新的字符串对象,而不是更改现有的对象。
有时,一个类型将以两种形式提供,一种是可变形式和一种是不可变形式。例如,字符串生成器是字符串的一个可变版本(但e2肯定不是相同的Java类型,并且不可互换)。

三、设计一个抽象的类型
设计一个抽象类型包括选择良好的操作和确定它们应该如何行为。—最好有一些可以以强大的方式组合的简单操作,而不是许多复杂的操作。每个操作都应该有一个定义良好的目的,并且应该具有一个一致的行为,而不是一个特殊情况的全层。例如,我们可能不应该向列表中添加一个和操作。它可能会帮助那些使用整数列表的客户,但是字符串列表呢?还是有嵌套的列表?所有这些特殊情况都将使总和难以理解和使用
这组操作应该是足够的,因为必须有足够的操作来做客户端可能想要做的计算类型。一个很好的测试是检查是否可以提取该类型的对象的每个属性。例如,如果没有get操作,我们将无法找出列表中的元素是什么。基本信息不应该异常难以获得,例如,大小方法对列表不是严格必要的,因为我们可以在失败之前应用获取直到增加的指数,但这是低效和不方便的。
该类型可以是泛型的:例如,列表、集合或图。也可能是特定于领域的:街道地图、员工数据库、电话簿等等。但它不应该混合通用的和特定于领域的特性。旨在表示扑克牌序列的贴牌类型不应具有接受任意对象的通用添加方法,如整数或字符串。相反,将像交易卡这样的特定领域的方法放到通用类型列表中是没有意义的。

四、表示独立性
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。抽象类型的使用独立于其表示(用于实现它的实际数据结构或数据字段),因此表示中的更改对抽象类型本身之外的代码没有影响。例如,列表提供的操作与列表表示为链列表还是数组无关。您完全不能更改ADT的表示,除非其操作完全指定了前提条件和后置条件,以便客户知道要依赖什么,并且您知道可以安全地更改什么。
因为此数据类型是不可变的,所以子字符串操作并不需要将字符复制到新的数组中。它可以只指向原始的MyString对象的字符数组,并跟踪新的子字符串对象所表示的开始和结束。

五、测试抽象数据类型
我们通过为ADT的每个操作创建测试,为其构建一个测试套件。这些测试不可避免地会相互作用。测试创建者、生产者和诱变者的唯一方法是在产生结果的对象上调用观察者,同样,测试观察者的唯一方法是创建供他们观察的对象。测试creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec; § 测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确。风险:如果被依赖的其他方法有错误,可能导致被测试方法的测试结果失效。

六、保持不变量
良好的抽象数据类型的最重要特性是它保留了自己的不变量。不变量是程序的属性,对于程序的每个可能的运行时状态。不变性是一个关键的不变性:一旦创建,不可变对象应该在其整个生命周期中始终代表相同的值。说ADT保留了它自己的不变量意味着ADT负责确保它自己的不变量保持不变,这并不依赖于其客户的良好行为,正确性并不依赖于其他模块。
当ADT保留了自己的不变量时,对代码的推理就变得容易得多,如果可以指望字符串永远不会更改,则可以在调试使用字符串的代码时或尝试为其他使用字符串的ADT建立不变量时排除这种可能性。与一个字符串类型相比,它保证只有当它的客户端承诺不更改它时,它才会是不变的。然后,您必须检查代码中可能使用该字符串的所有位置。
多变量类型通常有一个复制构造函数,它允许您创建一个复制现有实例值的新实例。在这种情况下,Date的副本构造函数使用的时间戳值,自1970年1月1日起以毫秒为单位进行测量。作为另一个示例,字符串生成器的副本构造函数采用一个字符串。复制另一种可变对象的方法是克隆(),某些类型支持,但不是全部支持。
通常,您应该仔细检查所有ADT操作的参数类型和返回类型。如果任何类型是可变的,请确保实现不会返回对其表示的直接引用。这样做会创造出表示泄露。
除非迫不得已,否则不要把希望寄托于客户端上,ADT有责任保证自己的invariants,并避免“表示泄露”。一个更好的解决方案是更喜欢不可变的类型。如果我们使用了一个不可变的日期对象,比如java.time.ZonedDateTime,而不是可变的java.util.Date,那么我们将在讨论了公共和私有内容后结束这个部分。不可能再有代表泄露了。

七、表示的不变性和抽象函数
R:表示值(代表值)的空间由实际实现实体的值组成。ADT将作为单个对象实现,但更常见的是需要一个小的对象网络,因此该值通常相当复杂。
A: 抽象值的空间由该类型被设计为要支持的值组成。它们是柏拉图式的柏拉图式实体,但它们是我们想要查看抽象类型元素的方式。例如,无界整数的抽象类型可能有数学整数作为其抽象值空间;它可能被实现为原元(有界)整数数组,例如,与该类型的用户无关。
抽象类型的实现者必须对表示值感兴趣,因为实现者的任务是使用代表值空间来实现抽象值空间的错觉。
实现抽象类型的目的是支持对抽象值的操作。据推测,我们需要能够创建和操作所有可能的抽象值,因此它们必须是可表示的。发生这种情况是因为该表示法不是一个严格的编码。有多种方法可以将无序字符集表示为字符串。在这种情况下,未映射字符串“abbc”。在这种情况下,我们已经决定,该字符串不应该包含重复项。这将允许我们在命中特定字符的第一个实例时终止移除方法,因为我们知道最多可以有一个实例。
抽象函数:R和A之间映射关系的函数,即如何去解释R中的每一个值为A中的每一个值。图中的圆弧显示了抽象函数。在函数的术语中,性质可以说函数是上射的(也称为),不一定是单射的(一对一的),因此不一定是双射的,通常是局部的。RI告诉我们空间R中的r是否被AF映射到了空间A中的某个值。RI形成了空间R的一个子集(子集中的所有元素均被AF映射到了空间A中),表示不变性RI:某个具体的“表示”是否是“合法的”,也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值,也可将RI看作:一个条件,描述了什么是“合法”的表示值。
仅抽象值空间并不能确定AF或RI:同一抽象类型可以有几种表示。一组字符可以平等地表示为一个字符串,如上所述,或作为一个位向量,每个可能的字符都有一位。显然,我们需要两个不同的抽象函数来映射这两个不同的代表值空间。
为代表定义一个类型,从而为代表值的空间选择值,并不确定哪些代表值将被认为是合法的,以及那些合法的,将如何解释它们。即使对于代表值空间的相同类型和相同的代表不变RI,我们仍然可以用不同的方式来解释代表,使用不同的抽象函数AF。
关键是,设计一个抽象类型不仅意味着选择两个空间——规范的抽象值空间和实现的代表值空间——还意味着决定使用哪些代表值以及如何解释它们。在代码中写下这些假设非常重要,以便未来的程序员(和未来的自己)了解这些表示的实际含义。
代表不变变量不仅仅是一个简洁的数学思想。如果实现在运行时断言代表不变量,那么您可以尽早捕获错误。您当然应该调用checkRep()来在每个创建或突变代表的操作(创建者、生产者和不变量。Observer方法可以不用,但建议也要检查,以防止你的“万一”。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值