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

时间:2010-02-01 15:22:10

标签: java static api-design

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

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

    }
}

11 个答案:

答案 0 :(得分:9)

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

答案 1 :(得分:3)

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

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

答案 2 :(得分:3)

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

答案 3 :(得分: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点的结果。使用静态方法,该方法更明确(尽管不能保证!)纯粹和代表点没有被修改。

答案 4 :(得分:1)

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

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

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

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

答案 5 :(得分:1)

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

在不可变类上使用add()和subtract()等方法来返回不可变类的新实例是很常见的。这对于类似FP的编程来说是一种很好的风格,对于像这样的类来说非常合理。 (请参阅BigInteger或BigDecimal以获取很好的示例。请不要查看日期或日历,以查看糟糕的可怕示例。)

在类中保留方法允许您选择性地定义这些类可能实现的接口,使用Decorator或Adapter模式,编写某些类型的测试等。

答案 6 :(得分:0)

这些功能自然必须是非静态的。但如果你怀疑请参考GRASP,他们会描述这样的事情。

根据GRASP信息专家的说法,这些功能不应该是静态的。

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

  

信息专家将引导我们   把责任放在课堂上   需要的最多信息   实现它。

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

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

答案 7 :(得分:0)

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

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

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

答案 8 :(得分:0)

使它们静止也会使它们更难以进行单元测试!我在.NET中知道的唯一可以处理这个问题的模拟框架是TypeMock。

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

答案 9 :(得分:-1)

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

答案 10 :(得分:-2)

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

编辑:我投了票。没关系。我并没有试图给出一些简单的建议,比如把静态放在前面的方法中。在这种情况下,实例方法更好,但实际上并没有单一的答案。这取决于您的偏好。