9

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

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) {

    }
}
4

11 回答 11

9

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

于 2010-02-01T15:47:07.507 回答
3

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

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

于 2010-02-01T15:47:45.663 回答
3

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

于 2010-02-01T15:56:10.103 回答
2

从语义上讲,静态方法似乎更有意义。两者当然都可以工作,但是非静态方法使一个点优先于另一个点,并且进一步暗示 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 以添加点 2 的结果。使用静态方法,更清楚(尽管不能保证!)该方法是纯的,并且代表点没有被修改。

于 2010-02-01T15:47:06.723 回答
1

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

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

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

于 2010-02-01T16:22:34.017 回答
1

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

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

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

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

于 2010-02-01T15:23:34.283 回答
0

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

根据GRASP Information Expert 的说法,这些功能不应该是静态的。

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

信息专家将带领我们将职责放在具有履行职责所需的最多信息的班级中。

如果你让你的方法静态,你将把你的逻辑从实际数据中移开,并且必须将数据传递给方法。

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

于 2010-02-01T15:25:00.847 回答
0

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

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

于 2010-03-18T19:50:05.877 回答
0

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

  • 这些方法显然应该是 Point 方法的一部分,因为它们专门处理点
  • 对于使用两个点的方法,没有任何关于它们的信息表明他们需要关于其中一个点的信息比另一个点更多……所以没有关于该方法将是哪个实例的非静态成员的推送。

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

于 2010-02-01T15:45:19.723 回答
-1

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

于 2010-02-01T15:56:38.997 回答
-2

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

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

于 2010-02-01T15:29:36.010 回答