【问题标题】:What to call factory-like (java) methods used with immutable objects什么调用与不可变对象一起使用的类工厂 (java) 方法
【发布时间】:2011-01-04 00:36:05
【问题描述】:

当为“不可变对象”创建类时,不可变意味着实例的状态不能改变;在构造函数中分配的所有字段)在 Java(和类似语言)中,有时仍然允许创建修改后的实例很有用。也就是说,使用一个实例作为基础,并创建一个仅相差一个属性值的新实例;来自基本实例的其他值。举一个简单的例子,可以有这样的类:

public class Circle {
  final double x, y; // location
  final double radius;

  public Circle(double x, double y, double r) {
    this.x = x;
    this.y = y;
    this.r = r;
  }

  // method for creating a new instance, moved in x-axis by specified amount
  public Circle withOffset(double deltaX) {
    return new Circle(x+deltaX, y, radius);
  }
}

那么:应该调用什么方法“withOffset”? (注意:不是它的名字应该是什么——而是这类方法叫什么)。 从技术上讲,它是一种工厂方法,但不知何故,这对我来说似乎不太正确,因为工厂通常只被赋予基本属性(并且是静态方法,或者不是结果类型的成员而是工厂类型)。

所以我猜应该有一个更好的术语来形容这种方法。既然这些方法都可以用来实现“fluent interface”,也许它们可以是“流利的工厂方法”? 更好的建议?

编辑:正如其中一个答案所建议的那样,java.math.BigDecimal 是一个很好的例子,它的“添加”、“减去”(等)方法。

另外:我注意到this question(由 Jon Skeet 撰写)有点相关(尽管它询问方法的具体名称)

编辑,2014 年 5 月:我目前最喜欢的是 mutant factory,FWIW。

【问题讨论】:

  • 我会称其为名称不佳的方法。
  • "move" 和 "delta" 暗示现有实例属性的可变性...
  • 请阅读实际问题——我没有问如何命名方法,而是问了如何称呼各种方法。但为了防止其他人在场边绊倒,我会稍微修改一下代码。
  • 这让人想起写时复制语义......也许其中有一些东西。我知道这不是一个有用的答案,但也许它会击中某人大脑中的神经元并产生更好的答案:)

标签: java oop terminology immutability


【解决方案1】:

我将这些类型的方法称为“复制方法”

clone() 方法创建一个精确的副本,而 copy 方法创建一个实例的副本,通常具有隐含或显式的变化。例如,String#toUpperCase() 将是不可变 String 类的复制方法 - 它复制具有变体的实例:它将所有字母大写。

我认为您示例中的 withOffset() 方法是一种类似的复制方法。

我不知道有任何参考文献记录了“复制方法”这一术语。我从它在 C++ 中的使用中借用了术语“复制”:复制构造函数和来自 Taligent 编码标准 (more info) 的“复制”命名指南。


至于术语“流利的工厂方法”,我不知道为什么“流利”会有所作为,因为“流利的接口”只是一种 API 样式(与构建器模式分开)。如果术语“工厂方法”在这里不适用,我不明白称它为“流利的工厂方法”如何使它更好地适用。

【讨论】:

  • Fluent 部分只是一个建议,基于这样的方法通常用于支持 fluent 样式的事实。然而,它与流式风格不要求(甚至不一定促进)不变性不同。 “复制方法”听起来很合理,让我们看看是否有人有更好的建议。 (实际上我正要提到“复制构造函数”作为有问题的参考,但决定反对它以尝试消除可能的混淆)
【解决方案2】:

Hrm……它创建了对象的变异版本……也许我们应该称它为变异工厂? :-)

【讨论】:

    【解决方案3】:

    这看起来不像工厂方法。 单独的签名只告诉我可以在不同的调用中链接它,而不是它应该创建一个新实例: 我认为这个用例更像是一个允许 append("a").append("b") 的 StringBuilder。 当然,它每次都可以返回一个新的 StringBuilder(就像你的 Circle 一样)。

    所以它不是设计为工厂。 (考虑提取接口并为该方法编写 JavaDoc:“必须返回一个新实例,因为我是不可变的”——为什么会这样??)您的类是不可变的这一事实只是一个实现细节)。

    编辑: Perahs 一个更好的例子是 BigInteger,因为它也是不可变的。与 multiply(BigInteger) 一起,它提供了一个包私有方法:

    BigInteger multiply(long v)
    

    它返回一个新实例并且非常类似于您的案例。这只是一个恰好返回与初始对象相同类型的结果的操作;不是工厂,我觉得这种操作真的配不上它自己的名字。

    【讨论】:

    • 我同意它不是工厂方法,这就是为什么我希望找到一个更好的术语。 BigInteger 就是一个很好的例子。但除此之外,请注意,不变性在这里是一个非常重要的方面;绝对不是(只是)一个实现细节——整个对象必须是不可变的,而不仅仅是使用此方法的操作。也许你不熟悉不可变对象的风格或好处?它极大地帮助了多线程和并发数据结构。使用 StringBuilder,返回实例只是一个方便的东西,它允许链接。
    • 我从未说过该方法是不可变的,在我的帖子中,不变性是指您的 Circle 类的对象。我说“实现细节”是因为我试图只查看该方法以便对其进行分类并忽略实际实现。我要说的是,如果这是一个抽象方法(在接口中声明),那么您不太可能需要 all 实现来表现得像这样。因此,我认为这种行为对于这种方法来说不是必需的。
    • 我想我找到了一个更好的例子:String.concat()——尽管它不是来自 CharSequence 接口。 javadoc 说:“如果参数字符串的长度为 0,则返回这个 String 对象”。如果 X 偏移量为 0,您也可以这样做,因此甚至不需要新对象。
    • 当然,也可以返回相同的实例,如果进行次要优化,这很有用。实例的不变性是关键特性,我的观点是这些方法是支持许多用例所需的组成部分。并且需要一个名称/术语来讨论这些方法的类——我不关心方法本身的 javadoc,而是讨论实际使用或设计模式。
    • 我对细节有点迷失了,也许这就是你误会我的原因......我的全部意思是这些方法不值得拥有自己的名字,更不用说设计模式了。它们经常由于不变性而发生,但不仅如此。返回相同类型的(新)实例只是某些操作的特殊性(但我会说它仍然只是像其他任何方法一样)。 Bear.giveBirthToCub() 将返回一个新的 Bear 实例,但这并不比 Bear.eat() 更特别(而且 Bear 不是一成不变的——除了冬季,继续我的痴迷)。 :)
    猜你喜欢
    • 2013-04-18
    • 1970-01-01
    • 2013-08-30
    • 1970-01-01
    • 2012-11-18
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多