软构--ADT

文章探讨了抽象数据类型(ADT)的概念,强调表示独立性在防止表示泄漏和确保客户端代码与实现解耦中的重要性。ADT通过抽象函数和表示不变量来定义,提供了数据操作而非内部表示。区分了可变与不可变类型,如String的不可变性和List的可变性,并详细说明了构造器、生产器、观察器和变值器等操作的分类。设计良好的ADT应遵循简洁、一致的操作原则,并确保表示独立,以保护内部实现不受客户端影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

ADT的特性:表示泄漏、抽象函数AF、表示不变量RI

通过abstraction function and rep invariants,一个类实现的ADT意味着什么

abstraction function为我们提供了一种清晰地定义抽象数据类型上的相等操作的方法
rep invariants将更容易捕获由损坏的数据结构引起的错误。

Abstraction and User-Defined Types

User-Defined Types

除了编程语言所提供的基本数据类型和对象数据类型,程序员可定义 自己的数据类型

数据抽象Data Abstraction

数据抽象:由一组操作所刻画的数据类型

传统的类型定义:关注数据的具体表示

抽象类型:强调“作用于数据上的操作”,程序员和 client无需关心数据如何具体存储的,只需设计/使用操作即可

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

比如,我们在讨论List类型时,不考虑其数据结构,而是考虑到operation,get、size等等

Classifying Types and Operations(分类)

Classifying Types:Mutable and immutable type(可变和不可变数据类型)

不论是内置的还是用户定义的类型,可以被分为可变和不可变数据类型

mutable可变数据类型:是可以被更改的,意味着你在使用这个数据类型的一些operation的时候,可能会导致对该object的其他操作结果的更改。

比如,Date是可变的,当我们调用操作setMonth,会对Data的一个实例的Month更改,随后在使用getMonth的时候,会得到不同的值

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

比如:String是immutable的,因为它的操作都会创建一个新的String对象而不是改变现有的那个实例对象

一般,一种类型会提供两种形式,可变和不可变的,比如字符串有String和StringBuilder(可变的)

Classifying the operations of an abstract type

构造器Constructor:

  • 一个构造器可以接受参数,但是不能以被构建的对象作为参数
  • 也可以不是构造器
  • 可能实现为构造函数或静态函数
    • 如果以静态方法来实现creator,该方法叫做工厂方法factory method
    • String.valueOf(Object Obj)这个也是工厂方法的例子
  • t* → T

生产器(Producers):从该类型的旧对象生成新的对象

  • 比如String的concat(),它接受两个旧字符串,拼接这两个旧字符串,产生这个拼接后的新字符串
  • T+, t* → T

观察器(Observers):获取抽象类型的对象,返回不同的对象(个人理解:获取一个抽象对象的内部属性的类型)

  • 例如List的size(),返回一个int
  • T+, t* → t

变值器(Mutators):改变对象属性的方法

  • 例如,List的add()方法,可以通过添加元素改变list
  • 变值器通常返回void
    • 如果返回值为void,则必然意 味着它改变了对象的某些内部状态
  • 变值器也可能返回非空类型
    • 比如Set.add()返回一个boolean类型来表明这个集合是否改变
  • T+, t* → void | t | T

T是一个抽象类型本身
t是一些其他的类型
+标记表示该类型可能在签名的该部分出现一次或多次。
*标记表示它出现零次或多次。

Abstract Data Type Examples

int immutable

是不可变的,没有mutators
creators: the numeric literals 0 , 1 , 2 , …
producers: arithmetic operators + , - , * , /
observers: comparison operators == , != , < ,>

String immutable

– creators: String constructors
– producers: concat , substring , toUpperCase
– observers: length , charAt

List mutable

– creators: ArrayList and LinkedList constructors, Collections.singletonList
– producers: Collections.unmodifiableList
– observers: size , get
– mutators: add , remove , addAll , Collections.sort

Designing an Abstract Type

设计好的ADT,靠“经验法 则”,提供一组操作,设计其行为规约 spec,规约需要满足以下原则

  1. 设计简洁、一致的操作
    1. 操作要简单,少,可以是一些复杂方式的组合,而不是给出复杂的操作
    2. 每个操作都要有一个明确的目的、连贯的行为
  2. 要足以支持client对数据所做的所有操作需要,且 用操作满足client需要的难度要低
  3. 要么抽象、要么具体,不要混合 — 要么针对抽象 设计,要么针对具体应用的设计

RI(Representation Independence)

表示独立性:client使用ADT时无需考虑其内部如何实 现,ADT内部表示的变化不应影响外部spec和客户端
除非ADT的操作指明了具体的pre和post-condition,否则不能改变ADT的内部表示——spec规定了 client和implementer之间的契约
个人理解:表示独立性就是为了防止泄露,由于client只关注rep,不关注内部实现,因此对于内部实现部分,我们不能把它暴露出来,应该要防止其被篡改,因此,一方面,内部属性要private,另一方面,在get的时候,也要防止泄露发生,根据返回数据的类型自行选择是否需要复制一份新的数据返回

Test

  • 测试creators, producers, and mutators:调用observers来观察这些 operations的结果是否满足spec
  • 测试observers:调用creators, producers, and mutators等方法产生或 改变对象,来看结果是否正确。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值