繁体   English   中英

不可变类/对象,私有构造函数,工厂方法

[英]Immutable class/object, private Constructor, factory method

已经阅读了如何通过以下步骤使类成为不可变的

  1. 不要提供“setter”方法 - 修改字段引用的字段或对象的方法。
  2. 使所有字段成为最终和私有。
  3. 不允许子类重写方法。 最简单的方法是将类声明为final。 更复杂的方法是使构造函数私有并在工厂方法中构造实例。
  4. 如果实例字段包含对可变对象的引用,则不允许更改这些对象:
    一个。 不要提供修改可变对象的方法。
    不要共享对可变对象的引用。 永远不要存储对传递给构造函数的外部可变对象的引用; 如有必要,创建副本并存储对副本的引用。 同样,必要时创建内部可变对象的副本,以避免在方法中返回原始对象。

AM不确定我是否清楚地了解私有构造函数和工厂方法在不变性方面的效用。 如果我让类最终,基本上是关闭任何其他类扩展它的所有路径。 如何陈述更复杂的方法

我在Singleton模式中看到了私有构造函数,工厂方法,这是有道理的。 但是当我们谈论对象不变性时,当我们提到私有构造函数和静态工厂方法时,我们是否也限制了对象构造/实例化?

首先,有几个原因通常不应该覆盖不可变类,你可以在这里找到它们。

也就是说,将构造函数设为私有只是阻止类被覆盖的一种方法。 为什么? 因为在子类中,每个构造函数(隐式地)都调用super() ,它是基类的默认构造函数。 但是如果你将这个构造函数设为私有,则子类不能调用它,因此不能覆盖基类。 当您想要控制特定类的实例总数时,此方法非常适合,例如在单例的情况下。

我认为这里的重大问题是未来的重构。 假设,在更高版本中,如果您可以将MyClass的一些新特殊情况拆分为子类MySpecialClass,则会发现它会更简单。

如果MyClass是一个带有公共构造函数的可变类,您可以这样做并告诉用户新功能以创建一个新的MySpecialClass。 现有用途不受影响。

如果MyClass有私有构造函数和工厂方法,则没有问题。 您声明MySpecialClass嵌套在MyClass中,也使用私有构造函数。 添加和/或修改工厂方法以选择要创建的方法,但要确保现有的调用继续兼容。

如果MyClass是不可变的,那么你会做什么,最后,但是有一个公共构造函数?

是的,你是对的。 将构造函数设为私有没有任何意义。 通过这样做,我们限制了实例创建,这不是不可变的期望场景。

在sun网站中提到的示例中没有使构造函数私有http://docs.oracle.com/javase/tutorial/essential/concurrency/syncrgb.html

IMMUTABILITY - 非常有助于并发,因为它避免了在线程环境中创建各种不变量

工厂方法 -只是一个命名规则,因为它们更详细阅读和容易从他们的自定义名称理解。 例如: copyOf()方法比创建复制构造函数更有意义。 正如Joshua Bloch在Effective Java中所说的那样

私有构造函数 - 它们在像Singleton这样的模式中有自己的用途,但也有其自身的局限性。

仔细阅读以下几点

不要允许子类过载方法。 最简单的方法是声明类final。 更复杂的方法是使构造函数私有并在工厂方法中构造实例。

我认为这里的重点不是

使类最终和私有构造函数。

重点是

要么让你的班级成为最终的,要么拥有私人构造函数。

希望能帮助到你!!

资源

我自己的一些发现来自Effective Java Item 15,粘贴了相同的相关陈述

“回想一下,为了保证不变性,一个类不能允许自己被子类化。通常这是通过使类最终完成,但还有另一种更灵活的方法来实现它。使不可变类最终的替代方法是制作它的所有构造函数私有或包私有,并添加公共静态工厂来代替公共构造函数(第1项)。

虽然这种方法并不常用,但它通常是最好的选择。 它是最灵活的,因为它允许使用多个包私有实现类。 对于位于其包之外的客户端,不可变类实际上是最终的,因为不可能扩展来自另一个包并且缺少公共或受保护构造函数的类。 除了允许多个实现类的灵活性之外,这种方法还可以通过改进静态工厂的对象缓存功能来调整后续版本中类的性能。

然后讨论静态工厂与构造函数的优点

私有构造函数背后的想法是,您希望隐藏类数据的不可变实现,同时允许构建具有相同内部类型的不同数据的新实例。

例如

public class Temperature
{
    private readonly double temperatureInCelsius;

    public Temperature(string temperatureInCelsius)
    {
         this.temperatureInCelsius = System.Convert.ToDouble(temperatureInCelsius);
    }
    private Temperature(double temperatureInCelsius)
    {
         this.temperatureInCelsius = temperatureInCelsius;
    }

    public Temperature AddCelsius(string temperatureToAddInCelsius)
    {
         return new Temperature(System.Convert.ToDouble(temperatureToAddInCelsius) + temperatureInCelsius);
    }
    public void PrintCelsius(Display display)
    {
        display.Print(temperatureInCelsius);
    }
    public void PrintFarenheit(Display display)
    // ... etc
}

忽略示例的半愚蠢,如果您的要求是可以从表示温度的字符串构造类。 它实际存储温度的方式可能会发生变化,并且是一个实现细节。 可以将此类更改为使用浮点数,字符串,双精度数,整数等。此方法保持不变性,同时允许实现的灵活性。 显然,当您包装更复杂的对象(如集合,词典等)时,这会变得更加强大。

这是因为使用私有构造函数我们不能创建它的子类,因此限制任何其他类的扩展它的所有路径。 它很复杂,因为它有自己的限制/复杂性,如单身模式。

暂无
暂无

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

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