软件构造之不变量

本文探讨了软件构造中的核心概念——不变量,包括不变性的意义,如何利用不变量来维护代码的正确性,并介绍了表示不变量和抽象函数在确保系统状态一致性中的应用。

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

不变量

一个好的抽象数据类型最重要的属性是它保留了它自己的不变量。对于程序的每种可能的运行状态,不变量都是程序中永远为真的属性。不变性是我们遇到的第一个至关重要的不变量:不可变对象一旦创建,在整个程序的生命周期内,他都应该保持相同的值。当抽象数据类型保留自己的不变量时,推理代码变得容易很多。如果我们可以确认字符串永远不变,那么当我们调试使用了字符串的代码时,就可以排除字符串发生改变的可能性

不变性

让我们看下边一个例子
我们应该如何保证这些Tweet对象是不可变的,即一旦创建了一个Tweet对象,其作者,消息和日期永远不会变?
对不变性的第一个威胁是客户可以直接访问类中的字段,当我们编写下边这样的代码时,我们可以直接对该对象进行修改。
这是表示外泄的一个简单例子,这意味着类外部的代码可以直接修改内部的表示。像这样的表示外泄不仅会威胁到不变量,而且对表示独立性也是一个威胁。但幸运的是,Java中提供了语言机制来处理这种表示外泄。
public和private关键字表明哪些字段和方法只能在类中访问,哪些可以从外部访问。final关键字保证在创建对象之后不会重新分配此不可变类型的字段。但这还不是结束,表示依旧可以外泄,让我们看下的例子:
retweetLater根据一个Tweet对象,并且使用该对象中的相同的消息返回另一个Tweet对象。这儿的问题是:getTimestamp调用返回对tweett引用的同一个Date对象的引用。 t.timestamp和d是同一个可变对象的别名。所以当Date对象被d.setHours()改变时,这也会影响t中的日期,如快照图所示。
Tweet的不变性已被打破。问题在于我们造成了表示外泄,让Tweet不能再保证它的对象是不可变的。我们可以使用防御性复制来防止这种风险,
可变类型通常有一个拷贝构造函数,它允许你创建一个新的实例来复制现有实例的值。在这种情况下,Date的拷贝构造函数使用1970年1月1日以来以毫秒为单位测量的时间戳值。
所以我们在getTimestamp的返回值中做了一些防御性复制。但这还不是结束! 还有代表外泄。 考虑这个(也是非常合理的)客户端代码:

此代码的目的是在一天中的24小时内推进单个Date对象,每小时创建一条推文。但请注意,Tweet的构造函数只保存传入的引用,因此所有24个Tweet对象最终都以同一时间结束,如此快照图所示。
又一次,Tweet的不变性已被违反。我们可以通过明智的防御性复制来解决这个问题,在构造函数,我们可以写下如下代码:

表示不变量和抽象函数

表示值的空间(或简称为rep值)由实际实现实体的值组成。在简单情况下,抽象类型将作为单个对象来实现,但更常见的是需要一个小型的对象网络,所以这个值实际上往往是相当复杂的。不过,现在,仅仅将其视为数值就足够了。
抽象值的空间由类型设计支持的值组成。例如,无界整数的抽象类型可能具有数学整数作为其抽象值空间,而且它可能由原始(有界)整数的数组实现。
当然,抽象类型的实现者必须对表示值感兴趣,因为实现者的工作使用代表值空间实现抽象值空间。
假设我们选择使用字符串表示一些字符
代表空间R包含字符串,抽象空间A是数学字符集。我们可以用图形方式显示两个值空间,用一个箭头从代表值指向它表示的抽象值。 关于这张图片有几点值得注意的地方。
每个抽象值被映射到一些代表值。实现抽象类型的目的是支持对抽象值的操作。因此,我们可能需要能够创建和操纵所有可能的抽象值,因此它们必须是可表示的。
一些抽象值可以被映射到多个代表值。发生这种情况是因为表示不是严格的,有多种方法可以将一组无序的字符表示为一个字符串。
并非所有的代表值都被映射。在这种情况下,字符串“abbc”未被映射。在这种情况下,我们已确定该字符串不应包含重复项。

在实践中,我们只能说明这两个空间及其关系的一些要素;整个图形是无限的,所以我们通过给出两件事来描述它:
将rep值映射到它们表示的抽象值的抽象函数:
        AF:  R --> A
图中的弧线表示抽象函数
将rep值映射到布尔值的rep不变量:
        IR :  R --> boolean
对于代表值r,当且仅当r由AF映射时,RI(r)才为真
表示不变量和抽象函数都应该记录在代码中,紧挨着代表本身的声明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值