表示不变量

2023-11-20

不变量

产生好的ADT设计,其中最重要的一点就是它会保护/保留自己的不变量。 不变量是一种属性,它在程序运行的时候总是一种状态,而不变性就是其中的一种:一旦一个不变类型的对象被创建,它总是代表一个不变的值。当一个ADT能够确保它内部的不变量恒定不变(不受使用者/外部影响),我们就说这个ADT保护/保留自己的不变量.

当一个ADT保护/保留自己的不变量时,对代码的分析会变得更简单。例如,你能够依赖字符串不变性的特点,在分析的时候跳过那些关于字符串的代码;或者当你尝试基于字符串建立其他的不变量的时候,也会变得更简单。与此相对,对于可变的对象,你将不得不对每一处使用它的代码处进行审查。

不变性
在这篇阅读的后面,我们会看到许多关于不变量的例子,现在我们先看一看不变性:

/**
 * This immutable data type represents a tweet from Twitter.
 */
public class Tweet {

    public String author;
    public String text;
    public Date timestamp;

    /**
     * Make a Tweet.
     * @param author    Twitter user who wrote the tweet
     * @param text      text of the tweet
     * @param timestamp date/time when the tweet was sent
     */
    public Tweet(String author, String text, Date timestamp) {
        this.author = author;
        this.text = text;
        this.timestamp = timestamp;
    }
}

我们应该怎么样做才能确保Tweet对象是不可变的(一旦被创建,author, message, 和 date都不能被改变)?

第一个威胁就是使用者可以直接访问Tweet内部的数据,例如:

Tweet t = new Tweet("justinbieber", 
                    "Thanks to all those beliebers out there inspiring me every day", 
                    new Date());
t.author = "rbmllr";

这就是一个表示暴露(Rep exposure)的例子,就是说类外部的代码可以直接修改类内部存储的数据。上面的表示暴露不仅影响到了不变量,也影响到了表示独立性,如果我们改变类内部数据的表示方法,使用者也会受到影响。
幸运地是,Java给我们提供了处理这样的表示暴露的方法:

public class Tweet {

    private final String author;
    private final String text;
    private final Date timestamp;

    public Tweet(String author, String text, Date timestamp) {
        this.author = author;
        this.text = text;
        this.timestamp = timestamp;
    }

    /** @return Twitter user who wrote the tweet */
    public String getAuthor() {
        return author;
    }

    /** @return text of the tweet */
    public String getText() {
        return text;
    }

    /** @return date/time when the tweet was sent */
    public Date getTimestamp() {
        return timestamp;
    }

}

其中, private 表示这个区域只能由同类进行访问;而final确保了该变量的索引不会被更改,对于不可变的类型来说,就是确保了变量的值不可变。

但是这并没有解决全部问题,表示还是会暴露!思考下面这个代码:

/** @return a tweet that retweets t, one hour later*/
public static Tweet retweetLater(Tweet t) {
    Date d = t.getTimestamp();
    d.setHours(d.getHours()+1);
    return new Tweet("rbmllr", t.getText(), d);
}

retweetLater 希望接受一个Tweet对象然后修改Date后返回一个新的Tweet对象。

问题出在哪里呢?其中的 getTimestamp 调用返回一个一样的Date对象,它会被 t.t.timestamp 和 d 同时索引。所以当我们调用 d.setHours()后,t也会受到影响,如下图所示:
在这里插入图片描述
这样,Tweet的不变性就被破坏了。这里的问题就在于Tweet将自己内部对于可变对象的索引“泄露”了出来,因此整个对象都变成可变的了,使用者在使用时也容易造成隐秘的bug。
我们可以通过防御性复制来弥补这个问题:在返回的时候复制一个新的对象而不会返回原对象的索引。

public Date getTimestamp() {
    return new Date(timestamp.getTime());
}

可变类型通常都有一个专门用来复制的构造者,你可以通过它产生一个一模一样的复制对象。在上面的例子中,Date的复制构造者就接受了一个timestamp值,然后产生了一个新的对象。另一个复制可变对象的方法是使用clone() ,但是它没有被很多类支持(译者注:标准库里面只有5%支持),在Java中,使用clone()可能会带来一些麻烦。你可以在 Josh Bloch, Effective Java, item 11, 或者 Copy Constructor vs. Cloning获得更多有关这方面的信息。
现在我们已经通过防御性复制解决了 getTimestamp返回值的问题,但是我们还没有完成任务!思考这个使用者的代码:

/** @return a list of 24 inspiring tweets, one per hour today */
public static List<Tweet> tweetEveryHourToday () {
    List<Tweet> list = new ArrayList<Tweet>(); 
    Date date = new Date();
    for (int i = 0; i < 24; i++) {
        date.setHours(i);
        list.add(new Tweet("rbmllr", "keep it up! you can do it", date));
    } 
    return list;
}

这个代码尝试创建24个Tweet对象,每一个对象代表一个小时,如下图所示:
在这里插入图片描述
但是,Tweet的不变性再次被打破了,因为每一个Tweet创建时对Date对象的索引都是一样的。所以我们应该对创建者也进行防御性编程:

public Tweet(String author, String text, Date timestamp) {
    this.author = author;
    this.text = text;
    this.timestamp = new Date(timestamp.getTime());
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

表示不变量 的相关文章

  • 3. C++ 数据类型

    目录 1 七种基本的 C 数据类型 2 C 中的变量定义 3 C 中的变量声明 4 C 变量作用域 局部变量 全局变量 1 七种基本的 C 数据类型 各种变量类型在内存中存储值时需要占用的内存 以及该类型的变量所能存储的最大值和最小值 注意
  • @ControllerAdvice 和 @ExceptionHandler注解处理全局异常

    ControllerAdvice 和 ExceptionHandler注解处理全局异常 处理全局统一异常 处理service层抛出异常的方法 异常体系 处理全局统一异常 在构建RestFul接口的今天 我们一般会限定好返回数据的格式 有利于
  • Nmap使用方法

    文章目录 1 Nmap简介 2 Nmap使用方法 3 扫描技术 4 端口指定和扫描顺序 5 举例 5 1 简单扫描 nmap ip 5 2 全面扫描 nmap A ip 5 3 探测指定端口的开放状态 5 4 探测N个最有可能开放的端口 5
  • 升级SQLite数据库

    一 步骤 1 在之前的基础上添加一张Category表 在onCreate 方法中执行建CREATE CATEGORY表语句 2 然后在onUpgrade 中执行两条drop语句 发现数据库表存在 就将已经存在的表格删除 再在onCreat
  • CISSP一次通过指南(文末附福利)

    2017年12月19日 在上海黄浦区汉口路亚洲大厦17层通过了CISSP认证考试 拖拉了一年 终于成绩还算令人满意 为攒人品将自己一年多的复习心得和大家分享 希望能够帮到需要考证的朋友 本文作者 i春秋签约作家 tinyfisher 欢迎与
  • 【JavaWeb】网络原理初识

    网络原理初识 计算机网络的历史 局域网和广域网 网络组件中的重要设备 网络通信基础 基本概念 协议分层 OSI七层模型 TCP IP五层 或四层 模型 封装和分用 发送方 接收方 三层转发和二层转发 计算机网络的历史 计算机最初是为了打仗而
  • python基础之数据类型知识(1)

    注释 注解 解释 说明文字而已 特征 注释只是用于说明的文字不会影响内容本身 作用 1 用于添加说明文字 方便阅读 2 用于调试程序 排查错误 分类 单行注释 多行注释 内容 或者 内容 代码 print hello world print
  • 利用JS实现简单的todoList(记事本)效果

    目录 1 实现效果展示 2 HTML代码 3 CSS代码 4 Javascript代码 该记事本程序利用HTML CSS JavaScript前端三大框架来实现 实现了记事本的添加 已完成和删除待办事项的基本功能 下面是程序实现的全部代码
  • 交通类SCI顶级期刊排名

    大多数期刊还是在IEEE http ieeexplore ieee org 或者ScienceDirect https www sciencedirect com 上面的 如果你是学生的话 可能从你们学校的图书馆可以进到这些网站去下载论文
  • Vue js引用警告 “export ‘default‘ (imported as ‘xxx‘) was not found

    问题原因 ES6 编译器识别问题 如果在public js这样写会有警告export default imported as xxx was not found export const myMixin 解决办法 修改组件中引用js的地方
  • Linux TCP链接查看和调整

    查看Linux的TCP连接数的方法如下 统计80端口连接数 netstat nat grep i 80 wc l 统计httpd协议连接数 ps ef grep httpd wc l 统计已连接上的 状态为 established 的TCP
  • Java终止线程的三种方式

    停止一个线程通常意味着在线程处理任务完成之前停掉正在做的操作 也就是放弃当前的操作 在 Java 中有以下 3 种方法可以终止正在运行的线程 使用退出标志 使线程正常退出 也就是当 run 方法完成后线程中止 使用 stop 方法强行终止线
  • R----dplyr包介绍学习

    dplyr包 plyr包的替代者 专门面对数据框 将ddplyr转变为更易用的接口 gt 来自dplyr包的管道函数 其作用是将前一步的结果直接传参给下一步的函数 从而省略了中间的赋值步骤 可以大量减少内存中的对象 节省内存 可惜的是应用范
  • 【理解springboot自动装配原理】

    理解springboot自动装配原理 最近读了小马哥 mercyblitz Springboot编程思想 核心篇 有了一些心得和感悟 分享给大家 1 官网介绍了激活自动装配的方法 文档提到激活自动化装配的注解 EnableAutoConfi
  • DAS、SAN、NAS存储连接方式详解

    1 直接访问存储DAS Direct Access Storage DAS将存储设备通过SCSI接口或光纤通道直接连接到一台计算机上 代表为磁盘阵列柜RAID 磁盘阵列柜是由多个硬盘按照不同的方式组合成一个大型的磁盘组 利用个别磁盘提供数据
  • Spring的xml文档配置

    1基于XML的注解配置
  • webpack 收集依赖、打包输出精简实现

    文章目录 安装babel插件 读取文件信息 获取当前js文件的依赖关系 广度遍历获取所有依赖图 生成浏览器可执行代码 安装babel插件 由于ES6转ES5中需要用到babel 所以要用到一下插件 npm install babel cor
  • MATLAB-DL6

    MATLAB DL6 步骤 交互式迁移貌似2020a才有 学会用analyze network 命令行式 迁移学习 冻结 freezeWeights createLgraphUsingConnections 数据增强 学习参数 函数大杂烩
  • SQL求解用户连续登录天数

    数据分析面试过程中 一般都逃不掉对SQL的考察 可能是笔试的形式 也可能是面试过程中面试官当场提问 当场在纸上写出 或者简单说一下逻辑 今天 就来分享一道面试中常常被问到的一类SQL问题 连续问题 无论是什么样的场景 只要是 连续 问题 那
  • TCP/IP协议之服务器端——华清远见

    咳咳咳 今天也是认真学习的一天 一 TCP IP协议是什么 TCP协议是一种以固连线为基础的协议 它提供两台计算机之间可靠的数据传送 TCP可以保证从一端数据传至连接的另一端时 数据能够确实送达 TCP协议适合可靠性比较高的场合 就像拨打电

随机推荐

  • 队列的几种实现方式

    队列简介 队列是一种特殊的线性表 特殊之处在于它只允许在表的前端 front 进行删除操作 而在表的后端 rear 进行插入操作 和栈一样 队列是一种操作受限制的线性表 进行插入操作的端称为队尾 进行删除操作的端称为队头 队列是一种最常用的
  • Android10(Q)系统源码编译

    Android10系统编译 一 硬件环境 二 软件环境 三 开始编译 四 遇到问题 一 硬件环境 在ubuntu18 04系统中下载编译android10 Q 源码需要如下条件 1 至少4G内存 小于4G内存编译源码期间的等待将会是很痛苦的
  • 【数学建模】数据处理问题

    一 插值与拟合 常用于数据的补全以及趋势分析 1 插值 总的思想 就是利用函数f x 若干已知点的函数值 求出适当的特定函数g x 这样f x 其他未知点上的值 就可以用g x 在这一点的值来近似 这种通过已知求未知的方法称为 插值 插值方
  • mysql知识系列:查看用户密码、修改用户密码,对网上“update user set authentication_string=‘123456’ where user=‘root’;”纠错

    说明 博主用的是mysql8 0 18 网上在找回mysql密码 清一色的教程都是修改root用户的密码 并且使用 update user set authentication string 123456 where user root 博
  • Keycloak概述

    这里写自定义目录标题 Keycloak概述 Single Sign On Kerberos 社交登录 用户合并 客户端适配 管理控制台 用户管理控制台 标准协议 授权服务 Getting Started Keycloak概述 keycloa
  • FPN网络详解

    1 特征金字塔 特征金字塔 Feature Pyramid Networks FPN 的基本思想是通过构造一系列不同尺度的图像或特征图进行模型训练和测试 目的是提升检测算法对于不同尺寸检测目标的鲁棒性 但如果直接根据原始的定义进行FPN计算
  • mysql报错ERROR 1356 (HY000): View ‘mysql.user‘ references invalid table(s) or column(s) or function(s)

    当您在使用 UPDATE user SET password PASSWORD newpassword WHERE User root 命令时提示 ERROR 1356 HY000 View mysql user references in
  • c语言数组下标和指针,C语言 数组 下标与指针 效率解析

    以字符串拷贝函数为例 解析数组中下标与指针的效率情况 指针的效率至少和下标相同 原因参考C下标的实现原理 注意编译器差异 因为部分编译器针对下标设置了特殊汇编指令 不做考虑 define SIZE 50 int x SIZE int y S
  • SQL中join group by having max() 时转Linq

    本来开发时有一个分组聚合的脚本 比较复杂 为了笔记效果 所以将脚本做一个简化 本来库里有两个表TableA和TableB 两个表的主键做如下关联 TableA的主键ID为TableB的外键Aid SELECT a Id a Name b I
  • 【Android11系统开发】上层app通过AIDL监听framework数据

    一 适用场景 在Android系统开发中 需要监听按键 触摸 或者可见窗口大小变化等需求时 你会考虑什么方法来实现呢 通过广播的方式可以实现 但是效果可能并不好 AIDL可以实现跨进程通讯 可以解决以上需求 下面重点分析下如何具体实现 以实
  • Node.js事件循环

    在 Node js 中 事件循环是用来处理非阻塞 I O 的基础 这意味着在 Node js 中 用户代码不会因为等待 I O 操作而停止执行 而是在 I O 操作完成后被通知 Node js 中的事件循环的工作方式有以下几种 首先 Nod
  • 【elementplus】body设置zoom后,el-table开启show-overflow-tooltip后,表格的tooltip显示会错位的解决方案

    由于我的项目是无法避免使用zoom 所以只记录zoom后的解决方案 示例 明明划过的是第一行 tooltip却显示到了第四行的位置 正确显示 划过第一行 tooltip显示在第一行的位置 代码 使用transform属性来修复el tabl
  • JavaScript 实现html导出为PDF文件

    相信各位前端工程狮们在一些报表项目 管理系统项目中都会遇到在这样的需求 申请报 表格 简历等等图文信息有导出为PDF文件 下面是记录我在项目中完成该需求的代码dome 发布出来也是希望对大家有些帮助 1 整体思路 将HTML元素打印或导出为
  • 【满分】【华为OD机试真题2023 JS】统计匹配的二元组个数

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 统计匹配的二元组个数 知识点数组 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 给定两个数组A和B 若数组A的某个元素A i 与数组B中的某个元素B j 满足 A
  • 函数getopt(),及其参数optind

    getopt被用来解析命令行选项参数 转载地址 http hi baidu com xlt1888 blog item 703148383008492670cf6c2d html include
  • java属于什么语言_java是什么语言 ?是什么系统?

    一开始了解计算机这个专业 大家都会经常性听到Java这一词语 那么大家有真正的了解什么是Java吗 Java是属于什么语言呢 JAVA语言 其实是混合型的一种语言 Java语言是一个支持网络计算的面向对象程序设计语言 Java语言吸收了Sm
  • MinIO学习文档(Java版)

    目录 一 安装 1 在k8s中安装minio单机版 1 创建minio名称空间 2 minio单机版安装yaml 二 代码 1 pom xml 说明 minio所用依赖 2 application yml 说明 放置minio连接信息 mi
  • Python爬虫入门——梦开始的地方

    目录 文章目录 前言 一 前置知识 二 实现步骤 1 分析网站 2 制定爬取方案 3 实现方案 4 基础代码展示 5 代码详解 6 代码封装 总结 前言 爬虫应严格遵守国家法律规定 时隔数月 进入暑假 回忆这数月的学习内容 不禁感慨计算机的
  • jpa insert 对象_SpringBoot2.x入门:使用JPA

    前提 这篇文章是 SpringBoot2 x入门 专辑的 第9篇 文章 使用的SpringBoot版本为2 3 1 RELEASE JDK版本为1 8 这篇文章会介绍一下SpringBoot如何引入和使用JPA JPA 即Java Pers
  • 表示不变量

    不变量 产生好的ADT设计 其中最重要的一点就是它会保护 保留自己的不变量 不变量是一种属性 它在程序运行的时候总是一种状态 而不变性就是其中的一种 一旦一个不变类型的对象被创建 它总是代表一个不变的值 当一个ADT能够确保它内部的不变量恒