不可变类的静态与非静态方法

Posted

技术标签:

【中文标题】不可变类的静态与非静态方法【英文标题】:static vs non-static method for immutable class 【发布时间】:2011-01-11 18:30:01 【问题描述】:

给定下面的类定义。如何决定存根方法应该是静态的还是非静态的?

class Point 
    private final int x;
    private final int y;

    public Point(int x, int y) 
        this.x = x;
        this.y = y;
    

    // Should the methods add(), subtract() and inverseOf() be non-static ...

    public Point add(Point point) 

    

    public Point subtract(Point point) 

    

    public Point inverseOf() 

    


    // Or static?

    public static Point add(Point point1, Point point2) 

    

    public static Point subtract(Point point1, Point point2) 

    

    public static Point inverseOf(Point point) 

    

【问题讨论】:

不,这不是家庭作业。我只是很难确定我的应用程序中的某些方法应该是静态的还是非静态的。我使用了一个简单的例子来确保我的应用程序的细节不会分散问题的关键元素,即静态与非静态。 我肯定会参加 final 的课程。另外,将构造函数设为private 并添加静态创建方法也是一个好主意。 【参考方案1】:

当方法体不依赖于任何一个特定实例时,使用静态方法。

作为一个例子,看看你的add(Point, Point) 方法。您将作为参数传递给函数的两个Points 相加,并返回另一个Point。这真的需要内部this 引用一些Point 吗?

另一方面,你有一个方法add(Point)。大概这会将函数参数添加到实例中 - 在这种情况下,您必须将其设为实例方法,以便您同时拥有 Points。

编辑:我想我最初误解了。回顾过去,您对静态和非静态实现都有正确的签名。在这一点上,我会说这是一个风格问题,因为你知道两者都可以正常工作。您希望如何使用您的积分等级?想想说Point a = Point.add(b, c)Point a = b.add(c) 是否会使代码更直观。就个人而言,我喜欢前者,因为它告诉我两个操作数都不会被修改。

【讨论】:

致反对者:留下评论来解释反对票通常是一种好习惯。 赞成,特别是因为我讨厌不解释为什么他们认为你错了的反对者......所以你只能假设这只是因为他们不同意你的意见(而不是事实问题)。 @RHSeeger - 确实如此。无法知道这是合法投诉还是 SCITE。【参考方案2】:

很自然,这些函数必须是非静态的。但如果您有疑问,请参考 GRASP,他们会这样描述。

根据GRASPInformation Expert,这些函数不应该是静态的。

尽管没有关于静态方法的直接信息,但有

信息专家将带领我们 将责任放在类中 所需的最多信息 实现它。

如果您将方法设为静态,您的逻辑将远离实际数据,并且必须将数据传递给方法。

删除静态将使逻辑更接近它使用的数据。

【讨论】:

在我看来,您链接的 GRASP 页面并没有真正表明方法是否应该是静态的,只是它们应该是相关类的方法(它们是在这两种情况下)。 为什么它们必须是实例方法?你能举出任何具体的原因吗? 静态方法无权访问实例数据。根据信息专家的说法,您应该将方法放置在他们可以访问的地方。删除静态使逻辑更接近数据。【参考方案3】:

在您的情况下,它必须是非静态的,除非您将签名更改为 public static Point add(Point point1, Point point2)

编辑:我被否决了。没关系。我并不是想给出一些琐碎的建议,比如将静态放在前面的方法中。在这种情况下,实例方法更好,但确实没有单一的答案。这只是取决于你的喜好。

【讨论】:

【参考方案4】:

我倾向于在这方面违背规范,但无论哪种方式对我来说都是合理的。

这些方法显然应该是 Point 方法的一部分,因为它们专门处理点 对于使用两个点的方法,没有任何关于它们的信息表明他们需要更多关于其中一个点的信息... .

对于像 Java 这样的语言,我会使用静态方法,特别是因为上面的第二点。对于具有运算符重载的语言(如 Ruby),我会使用实例方法来利用它。

【讨论】:

致反对者:留下评论来解释反对票通常是一种好习惯。【参考方案5】:

从语义上讲,静态方法似乎更有意义。两者当然都可以工作,但是非静态方法优先于一个点,并且进一步暗示 point1(调用 add 的方法)可能会因调用而被修改。

作为使用您的课程的开发人员,如果我看到以下内容:

Point p1 = new Point(1,2);
Point p2 = new Point(2,3);

p1.Add(p2);

或者..

Point p1 = new Point(1,2);
Point p2 = new Point(2,3);

Point.Add(p1, p2);

我的自然倾向是假设非静态版本中的 add() 方法修改了 point1 以添加 point 2 的结果。使用静态方法,更清楚(尽管不能保证!)该方法是pure 并且代表点没有被修改。

【讨论】:

使用非静态方法但将名称更改为加号和减号可能是一个很好的中间立场【参考方案6】:

我会选择实例方法。然后,您可以使方法成为接口的一部分并覆盖它们。当您必须处理 2d 点或 3d 点并且有一些客户端代码并不真正关心并且只需要对实现接口的点执行操作时,您将获得好处。

【讨论】:

自己只是在输入相同的答案。你更快,所以 +1 :-) 拥有静态方法的唯一理由是,如果您要进行大量调用且空点实例有效。【参考方案7】:

我认为这取决于您要完成的工作。如果您提供一种将任意两点相加的方法,那么您需要一个静态方法。但是,如果您想要一个将点添加到给定 Point 实例的方法,那么您需要一个非静态方法。

如果您确实使用静态方法,那么您可以考虑将静态方法放入仅包含静态方法的单独实用程序类(PointCalculator)中。这类似于 Math 类。

【讨论】:

鉴于该类是不可变的,我不明白“提供添加任意两个点的方法”和“将点添加到给定点实例的方法”之间有什么区别跨度> 静态方法表示您正在添加两个点以创建一个新点。非静态方法表明您正在修改现有点。这当然不是真的,因为您要返回一个新点。例如,用户可以编写 p1.add(p2); (用户可能认为他们已将 p2 添加到 p1 并且新值在 p1 中)而不是 Point p3=p1.add(p2) 但 Point p3=Point.add(p1, p2) 非常清楚。所以这是静态的一点。【参考方案8】:

即使您的Point 是不可变的,我也会选择更面向对象的非静态方法(是的,使用太多静态方法会破坏多态性、继承等对象的好处)。实际上,这与 BigDecimalBigInteger 这样的类的设计方式是一致的。最重要的是,static methods make classes harder to test 所以我宁愿尽可能避免使用它们,尤其是在有意义的时候。

【讨论】:

【参考方案9】:

这些方法应该是静态的,因为类本身适合通过构造函数创建并赋值一次,因为 x 和 y 是最终的。这意味着您可以创建点,但不能继续操作它们的数据。 Add/Substract/Etc 方法是实用方法,不需要使用 Point 的实例。

【讨论】:

定义看起来像 mutators 但在不可变对象上返回一个新实例的方法是完全合理的。这是一种常见的函数式编程实践,也是编写线程安全 Java 的好方法。【参考方案10】:

如果您打算使用 Java 并创建对象,那么从风格上讲,我认为您应该尝试最大限度地利用对象和数据封装。对我来说,这意味着将数据留在原处(在 Point 类中),而不是将其传递给单独的方法来处理它。让你的物品为你工作;不仅有 getter 和 setter。事实上,认真思考如何完全避免需要吸气剂。

在不可变类上使用 add() 和 subtract() 之类的方法返回不可变类的新实例是非常常见的。这对于类 FP 编程来说是一种很好的风格,对于这样的类来说是完全合理的。 (有关好的示例,请参见 BigInteger 或 BigDecimal。对于坏坏的可怕示例,请参见 Date 或 Calendar。:)

在类中保留方法让您可以选择定义这些类可能实现的接口、使用装饰器或适配器模式、编写某些类型的测试等。

【讨论】:

【参考方案11】:

将它们设为静态也会使对它们进行单元测试变得更加困难!我在 .NET 中知道的唯一可以处理此问题的模拟框架是 TypeMock。

如果打算使此类不可变,那么您将在任何访问器中返回新的 Point 对象,调用所以使它们成为静态在这里没有多大意义。

【讨论】:

以上是关于不可变类的静态与非静态方法的主要内容,如果未能解决你的问题,请参考以下文章

java中一个类是不可变类的条件是啥?求详细解答。

java中静态类和动态类的区别的啥?

PHP静态方法与非静态方法用法及区别

静态类的静态方法与非静态类的静态方法 (C#)

java 技巧

深入理解C# 静态类与非静态类静态成员的区别