【问题标题】:Prefer non-member non-friend functions... in Java? [closed]更喜欢非成员非朋友函数......在Java中? [关闭]
【发布时间】:2023-09-03 01:10:01
【问题描述】:

免责声明:这个问题是针对那些认为 Scott Meyers 在有效 C++ 的第 23 条中的建议是良好的 OO 设计的人——至少在 C++ 中是这样。

在不存在全局函数的 Java 中,这个原则起初似乎不适用,但在我看来确实适用。以 Scott Meyers 自己为例。

public class WebBrowser {
    public void clearCache() {}
    public void clearHistory() {}
    public void removeCookies() {}
}

通过创建一个包含 static 便捷方法的关联“命名空间”类,我通过最小化可以访问其内部的代码量来增加对WebBrowser 的封装。毕竟,Java 中的静态方法本质上是全局函数(假设类中的所有内容都是公共的和静态的)。

public class WebBrowserStuff {
    private WebBrowserStuff() {} // prevent instantiation

    public static void clearBrowser(WebBrowser browser) {
        browser.clearCache();
        browser.clearHistory();
        browser.clearRemoveCookies();
    }
}

我能看到的唯一缺点是 Java 中没有依赖于参数的查找,因此调用该方法会稍微冗长一些。

WebBrowserStuff.clearBrowser(browser);

我的问题是,鉴于在 C++ 中这种使用非成员函数是可取的(请参阅我的免责声明),除了增加冗长之外,还有什么理由不希望在 Java 中这样做?这个问题专门针对 C++ 和 Java 在这种技术方面的区别。

有兴趣听取个人意见,这是否是一个好的面向对象设计,尽管我有兴趣了解是否存在任何文化差异在 C++ 和 Java 之间,这可能会导致普遍意见偏向一种或另一种。

[编辑]

不幸的是,我并没有真正得到我的问题的答案,我试图减少基于意见的编辑并没有阻止它被关闭,所以我无法选择一个可接受的答案。可以将其解释为确实没有技术原因您不想这样做(假设这是 C++ 中的良好实践),任何反对这种技术的行为纯粹是个人的或 Java 文化的事情。

【问题讨论】:

标签: java c++ interface encapsulation convenience-methods


【解决方案1】:
WebBrowserStuff.clearBrowser(browser);

是按类定义的静态方法,并且不能直接访问所传递的实例之外的实例。出于实用性的原因,这需要 另一个 类。通常在 Java 的库本身中,只有当我们使用无法处理所有这些静态方法的专用类型时才会这样做(Array 对象与Arrays 类中的实用程序或Collections 相比,其中我们正在使用一系列不应采用静态方法的接口。

这些帮助类通常不是好的做法,在您的情况下,您可以并且应该clearBrowser 作为WebBrowser 的非静态方法。

现在,这已经完成了,虽然这不是很好的做法,但它在技术上是有效的,并且没有任何合同义务剥夺您这项权利。关于这一点,为了满足 Java 库的命名,我将调用辅助类 WebBrowsers,因为这些库通常采用有问题的类/接口并将其复数用于此命名目的。

【讨论】:

  • 你有理由说这些课程在实践中表现不佳吗?为什么我要把clearBrowser 作为WebBrowser 的非静态成员?而且我意识到静态方法没有与实例链接。这就是重点——它们的行为就像全局函数。我不完全确定出于实用性原因需要另一个课程是什么意思。也许您只是在总结 WebBrowserStuff 的作用?感谢您指出 Arrays 和 Collections 类;这些是很好的通用算法示例集合,非常适合单独的“静态”类。
  • @JosephThomson 1. 它创建了更多类。 1和2)通常对实例状态进行操作的方法应该在类中,而不是在另一个类中,因为它在“此浏览器的clearBrowser,而不是带参数的clearBrowser”中具有语义含义。
【解决方案2】:

可以接受,是的。首选,没有。原因如下:静态方法本质上被排除在实例化的开销之外——因此为 WebBrowser 类上的一些支持方法创建一个新类是非常愚蠢的。如果有的话,将静态方法添加到 WebBrowser 类。这避免了您所说的冗长,并将类似的东西放在一起。但是,我也同意 Colin 的观点,如果您将其构建为一种类型的库或稍后要扩展的东西,那么一开始就不要过度隐藏。

【讨论】:

  • 您所说的“静态方法在本质上被排除在实例化开销之外”是什么意思?这是性能问题吗? “把事情放在一起”也是让所有东西都成为 C++ 中的成员函数的主要论据,但我并不真正相信它(Meyers 在一篇在线文章中提出了很好的论点)。
  • 我的意思是静态方法不会为每个创建的实例花费额外的开销。因此,如果您同时运行 3000 个 Web 浏览器,则调用 3000 个 Web 浏览器所支付的开销与调用成员函数时所支付的开销不同。
【解决方案3】:

不,这在 Java 中并不常见,也不是这样做的方式。 您创建的 WebBrowserStuff 在 Java 中看起来像是单例模式的糟糕实现。 您的实现的一个问题是您可以创建多个 WebBrowserStuff 实例。看起来你只需要一个,所以你应该使用Singleton

想想你是否想在浏览器中拥有这些方法。在您的情况下,这似乎是正确的方法。它们应该是浏览器的一部分。

但是,如果您想创建一个辅助类,请确保将其设为 Singleton:添加一个私有构造函数,这样除了该类之外没有人可以创建实例并添加一个 getInstance() 方法来获取该实例。

【讨论】:

  • 你是对的,我应该将构造函数设为私有,但不是只有 WebBrowserStuff 可以创建自己的实例,而是没有任何东西可以创建它的实例; WebBrowserStuff 不是单例;它只是一堆静态方法的容器,没有状态,所以创建一个WebBrowserStuff 实例是没有意义的。
  • 约瑟夫,你当然可以使用静态类。我认为在这种情况下单例更好。检查这个关于 Singleton vs Class 和所有静态方法的讨论 *.com/questions/7329788/… 。我发现它非常有用。还要检查实现 Singleton 的 Enum 方式(从 Java 5 开始)。如果资源真的很短缺(小型移动设备?),我会使用全静态方法类。甚至对于像 Collections 这样的类的辅助方法也不行。单例具有更大的灵活性。干杯。