9

我在一些设计书籍中读到不可变类提高了可伸缩性,以及尽可能编写不可变类的良好实践。但我认为这样不可变的类会增加对象的增殖。那么使用不可变类或更好地使用静态类(所有方法都是静态的类)以提高可伸缩性是好事吗?

4

8 回答 8

16

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

另一个主要好处是,根据定义,不可变对象没有同步问题。这就是可伸缩性问题的来源。编写多线程代码很困难。不可变对象是(大部分)规避问题的好方法。

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

然而,静态工厂方法还有其他好处。这个想法是封装对象创建。

于 2009-08-16T16:47:45.453 回答
5

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

至于将类与所有静态方法一起使用,在大多数可以使用不变性的情况下,这并不是一个真正的选择。以 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; }
}

您将如何使用仅包含静态方法的类来实现这一点?

于 2009-08-16T19:43:57.947 回答
1

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

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

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

于 2009-08-16T17:08:44.560 回答
1

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

HashMap 和 HashSet 依靠这样一个事实,即只要对象在映射或集合中,对象的哈希码就会保持不变。如果您使用对象作为 HashMap 中的键,或者将其放入 HashSet,然后更改对象的状态以使 hashCode() 返回不同的值,那么您会混淆 HashMap 或 HashSet 并且你会得到奇怪的东西;例如,当您迭代地图或设置对象时,当您尝试获取它时,就好像它不存在一样。

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

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

于 2009-08-16T18:44:45.857 回答
0

不变性通常用于实现可伸缩性,因为不变性是 Java 中并发编程的促成因素之一。因此,正如您所指出的,虽然“不可变”解决方案中可能有更多对象,但它可能是提高并发性的必要步骤。

另一个同样重要的用途 og immutability 是使用设计意图;制作不可变类的人希望您将可变状态放在其他地方。如果你开始改变那个类的实例,你可能会破坏设计的初衷——谁知道后果可能是什么。

于 2009-08-16T17:25:36.110 回答
0

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

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

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

于 2009-08-16T17:45:00.713 回答
0

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

于 2009-08-16T17:52:33.707 回答
0

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

例如:

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

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

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

它应该输出:

abc

Because String is immutable, A will simply point to new "abc123" string object instead of modifying the previous string object.

于 2009-08-17T04:39:37.090 回答