繁体   English   中英

可变或不可变的类?

[英]Mutable or immutable class?

我在一些设计书中读到,不可变类提高了可伸缩性,并且尽可能地编写不可变类。 但我认为如此不可改变的阶级会增加对象的扩散。 因此,为了提高可伸缩性,使用静态类(具有所有静态方法的类)更好地进行不可变类或更好吗?

但是, 不可变类的主要好处是可以公开不可变的内部数据成员,因为调用者无法修改它们。 这是一个巨大的问题,比如java.util.Date 它是可变的,因此您无法直接从方法返回它。 这意味着你最终会做各种防御性复制 这增加了对象的扩散。

另一个主要好处是根据定义,不可变对象没有同步问题。 这就是可扩展性问题的来源。编写多线程代码很难。 不可变对象是(大多数)绕过问题的好方法。

至于“静态类”,通过你的评论,我认为它是指具有工厂方法的类,这是通常描述的方式。 那是一种无关的模式。 可变类和不可变类都可以具有公共构造函数或具有静态工厂方法的私有构造函数。 这对类的(im)可变性没有影响,因为可变类是在创建后可以更改其状态的类,而在实例化后不能更改不可变类的状态。

然而,静态工厂方法可以具有其他好处。 想法是封装对象创建。

不可变类确实会促进对象扩散,但是如果你想要安全,可变对象会促进更多的对象扩散,因为你必须返回副本而不是原始副本以防止用户更改你返回的对象。

至于使用所有静态方法的类,在大多数可以使用不变性的情况下,这不是一个真正的选项。 从RPG中获取此示例:

public class Weapon
{
    final private int attackBonus;
    final private int accuracyBonus;
    final private int range;

    public Weapon(int attackBonus, int accuracyBonus, int range)
    {
        this.attackBonus = attackBonus;
        this.accuracyBonus = accuracyBonus;
        this.range = range;
    }

    public int getAttackBonus() { return this.attackBonus; }
    public int getAccuracyBonus() { return this.accuracyBonus; }
    public int getRange() { return this.range; }
}

您如何使用仅包含静态方法的类实现此操作?

正如cletus所说,不可变类简化了同步方法中的类设计和处理。

即使在单线程应用程序中,它们也简化了集合中的处理。 不可变类永远不会改变,因此键和哈希码不会改变,所以你不会搞砸你的收藏。

但是你应该记住你正在建模的东西的生命周期和构造函数的“重量”。 如果您需要更改该东西,则不可变对象变得更加复杂。 您必须替换它们,而不是修改它们。 并不可怕,但值得考虑。 如果构造函数需要非常重要的时间,那也是一个因素。

需要考虑的一件事是:如果您打算将类的实例用作HashMap中的键,或者如果您要将它们放在HashSet中,那么使它们成为不可变的更安全。

HashMap和HashSet指的是只要对象在map或set中,对象的哈希码就保持不变。 如果你将一个对象用作HashMap中的一个键,或者你把它放在一个HashSet中,然后改变对象的状态,以便hashCode()返回一个不同的值,那么你就会混淆HashMap或HashSet,你会得到奇怪的东西; 例如,当您迭代地图或设置对象时,但是当您尝试获取它时,就好像它不存在一样。

这是由于HashMap和HashSet如何在内部工作 - 它们通过哈希代码组织对象。

Java并发大师Brian Goetz 撰写的这篇文章很好地概述了不可变对象的优缺点。

不可变性通常用于实现可伸缩性,因为不可变性是Java中并发编程的推动因素之一。 因此,正如您所指出的那样,“不可变”解决方案中可能存在更多对象,这可能是提高并发性的必要步骤。

另一个同样重要的用途是使用不可变性来消耗设计意图 ; 制作不可变类的人意图将其置于其他地方。 如果你开始改变那个类的实例,你可能会破坏设计的初衷 - 谁知道后果可能是什么。

以字符串对象为例。 有些语言或类库提供可变字符串,有些则不提供。

使用不可变字符串的系统可以对具有可变字符串的系统进行某些优化。 例如,您可以确保只有任何唯一字符串的副本。 由于对象“开销”的大小通常远小于任何非平凡字符串的大小,因此这可能节省大量内存。 还有其他潜在的空间节省,如实习子串。

除了可能的内存节省之外,不可变对象还可以通过减少争用来提高可伸缩性。 如果您有大量线程访问相同的数据,则不可变对象不需要精心设计的同步过程来进行安全访问。

再考虑一下这个问题。 使用不可变对象允许您缓存它们而不是每次都重新创建它们(即字符串)它对您的应用程序性能有很大帮助。

我想,如果你想在不同的变量之间共享同一个对象,它需要是不可变的。

例如:

String A = "abc";
String B = "abc";

Java中的String对象是不可变的。 现在A和B都指向相同的“abc”字符串。 现在

A = A + "123";
System.out.println(B);

它应该输出:

abc

因为String是不可变的,所以A只会指向新的“abc123”字符串对象,而不是修改前一个字符串对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM