前言
软件构造的实验2主要为了训练ADT的设计与使用,包括训练抽象数据类型(ADT)的设计、规约、测试,以及设计每种ADT的表示 (representation)、表示不变性(rep invariant)、抽象过程(abstraction function),判断是否存在表示泄露(rep exposure)等等。由于在之前的编程中我并没有特别注意ADT编写的一些规范,没有特别区分Rep,RI,AF等等,因此在这次实验中我还是产生了许多感悟,接下来我就围绕ADT以及ADT设计来谈谈自己的感受。
ADT概念
什是ADT
ADT也就是抽象数据类型,它是一种模型,用于描述数据类型的抽象行为。它定义了一组数据和对这些数据执行的操作,而不暴露具体的实现细节。
ADT 将数据类型的逻辑特性与其具体实现分离,它关注的是数据类型应该如何被使用和操作,而不是如何被实现。它定义了数据类型的一组操作,这些操作可以对数据进行创建、访问、修改和删除等基本操作,以及其他高级操作。
也就是说,我们常用的栈(Stack)、队列(Queue)、链表(Linked List)、集合(Set)、映射(Map)等都是抽象数据类型。当然,我们也可以自己设计并实现抽象数据类型,以满足不同场景下的业务需求。
Rep, RI, AF, Safety from rep exposure
Rep, RI, AF, Safety from rep exposure,这些都是用于描述抽象数据类型(Abstract Data Type)的设计规范和目标。
-
Rep(Representation)
Rep 是指抽象数据类型的内部表示或数据结构。它定义了数据类型所使用的具体数据结构和组织方式。Rep 是 ADT 的实现细节,用户无需了解或直接访问它,而是通过 ADT 提供的操作接口来访问和操作数据。对于某两个ADT可能实现了同一种功能,为用户提供了同一套接口,但是它们内部实现的Rep可能完全不同。
-
RI(Representation Invariant)
RI 是指抽象数据类型的表示不变性,它是对 Rep 所代表的数据结构的一组约束条件或性质。RI 描述了 Rep 的合法状态和有效性规则,确保数据结构在操作过程中保持一致和有效。ADT 的操作实现应满足 RI,并在操作前后维护 RI 的正确性。
-
AF(Abstraction Function)
AF 是指抽象数据类型的抽象函数,它定义了数据类型的抽象视图和语义。AF 描述了数据类型的具体含义和目标,将 Rep 中的具体表示映射到抽象视图上。AF 帮助用户理解数据类型的行为和语义,以及如何正确地使用 ADT 的操作。例如,在一个ADT表示一个字典,那么如果选用HashMap来存放字典的字词以及解释时,那么就存在一个由HashMap的具体表示到一个抽象的字典的映射。
-
Safety from rep exposure
这是 ADT 设计的一个重要目标。它意味着用户无法直接访问或修改 ADT 的内部表示(Rep),而只能通过 ADT 提供的操作接口来操作数据。这样可以确保数据的一致性和完整性,防止用户直接访问或修改内部表示导致数据结构错误。
ADT可变性
在ADT设计过程中,我们常常需要区分ADT的可变性。对于ArrayList这样的ADT而言,由于其提供了修改其内部属性的方法,因此我们认为这个ADT是可变的;相反,对于String这种ADT而言,由于我们不能通过调用其提供的修改方法来更改它的值,我们只能通过重新赋值的方式来修改一个引用,因此这一类ADT就是不可变的。
ADT设计
说完了ADT,那就让我们仔细思考一下如何设计一个ADT。
确定可变性与不可变性
ADT的可变性与不可变性会给编程带来不同的好处,这是我们在实际设计ADT的过程中需要考虑的。首先对于可变的ADT来说,在实现业务功能时由于不需要重复创建销毁对象,只需要更改对象内部的属性即可,更为简单也有着更优的性能;而使用不可变数据类型又能够更加安全。所以我们需要权衡利弊,考虑实际业务场景,确定合适的类型。
设计ADT的几点原则
-
设计简洁,有一致的操作
我们应该选取一些通用的操作,而不应该去考虑过多的特殊情况。比如设计一个List,我们只需要考虑List的Add,Remove,Get等等操作,而不需要考虑列表装载Int型数据时的求和Sum操作——因为Sum操作对于String,Char等类型是无意义的。
-
要足以支持客户对数据所有的操作需要,并且用操作满足客户需要的难度低
我们应该仔细分析业务场景,把所有用户可能需要的操作封装进ADT中,同时对于一些客户虽然可以实现但需要频繁的使用的方法也封装好,便于用户更加便捷地使用。
-
设计ADT应该是独立于表示的
在设计ADT时,我们不应该让任何的内部表示暴露给外部,也就是说,更改内部表示不应该影响到外部的功能。
总结
设计好的 ADT 需要考虑接口设计、封装性、表示不变性、抽象函数、安全性、性能和测试等方面。一个好的设计能够提供清晰、可靠、高效和易用的数据抽象,为用户提供方便和可靠的数据操作接口。同时,只要我们能设计出好的ADT,一定会给我们的编码带来效率的提升。