JAVA学习笔记(三)ADT

本篇文章继续搬运MIT上ADT部分相关知识

Reading 11: Abstraction Functions & Rep Invariants

不变量

继续讨论实现好的抽象数据类型的方法,最后一条,可能也是最重要的一条,好的抽象数据类型的特性就是它保护自身的不变量。不变量是一种在程序每个可能的运行时刻始终保持正确的特性。不可修改性是我们已经遇到过的很重要的不变量:一旦创建,一个不可修改的对象应该在它的生命周期内始终代表相同的值。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类型是不可修改的?
第一个威胁到不可修改性的事实:没有什么能够阻挡下面的代码运行:

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

这是一个典型的表现泄露的例子,意味着类型外的代码可以直接修改该类型的表现。表现泄露不止威胁到不变性,也威胁到了表现独立性。我们不能在不影响客户端直接访问这些成员的情况下不改变Tweet的实现。
幸运的是,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为参数,并返回相同的tweet,但时间延后了一小时。
问题出在哪儿呢?getTimestamp方法返回了Date的引用,t.timestamp 和d都是相同的可修改对象的引用,所以我们可以通过d.setHours方法修改日期。
我们可以修复这种表现泄露通过防御性拷贝的方式:返回可修改对象的复制来避免引用泄露:

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

我们做了防御性拷贝之后,仍然存在表现泄露!考虑如下的客户端代码:

/** @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;
}

在这里插入图片描述
如上代码使用一个Date对象代表一天24小时,为每个小时都产生了一条tweet。但请注意每一个Tweet的构造器都保存了相同的Date引用,所以24个Tweet对象最后都表示了同一个时间
明智的方法是通过防御性拷贝初始化类:

public Tweet(String author, String text, Date timestamp) {
    this.author = author;
    this.text = text;
    this.timestamp = new Date(timestamp.getTime());
}
为可修改类型提供不可修改包装器

JAVA容器类提供了有趣的折中方法:不可修改包装器
**Collections.unmodifiableList()**以List作为参数,用一个看起来像List的类包装它,使修改者不可使用:set(),add(),remove()等等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值