效用类是邪恶的?

我看到了这个线程

如果一个“实用程序”类是邪恶的,我在哪里把我的通用代码?

想到为什么实用阶级是邪恶的?

比方说,我有一个领域模型是几十个类深。 我需要能够xml-ify实例。 我在父级上做了一个toXml方法吗? 我是否创buildMyDomainXmlUtility.toXml帮助器类? 这是业务需求跨越整个领域模型的情况 – 它是否属于实例方法? 如果在应用程序的xmlfunction上有一堆辅助方法呢?

实用类并不完全是邪恶的,但它们可以违反组成一个好的面向对象devise的原则。 在一个良好的面向对象的devise中,大多数类应该代表一个单一的东西,所有的属性和操作。 如果你正在做一件事情,那么这个方法应该可能是这个事情的一个成员。

但是,有时您可以使用实用程序类将多个方法组合在一起 – 例如, java.util.Collections类提供了许多可在任何Java Collection上使用的实用程序。 这些不是特定于某个特定types的集合,而是实现可用于任何集合的algorithm。

真的,你需要做的是想想你的devise,并确定哪里是最有意义的方法。 通常,它是一个类中的操作。 但是,有时候,这确实是一个实用的阶级。 但是,当您使用实用程序类时,请不要随意将其放入方法中 – 按目的和function组织方法。

我认为,普遍的共识是,公共事业本身并不是邪恶的。 你只需要明智地使用它们:

  • devise静态效用方法是一般的和可重用的。 确保它们是无国籍的; 即没有静态variables。

  • 如果你有很多实用的方法,把它们分成不同的类,这样开发人员很容易find它们。

  • 不要使用实用程序类,其中域类中的静态或实例方法将是更好的解决scheme。 例如,考虑抽象基类或可实例辅助类中的方法是否是更好的解决scheme。

  • 对于Java 8以后,接口中的“默认方法”可能是比实用程序类更好的select。


另一个看待这个问题的方法是观察引用的问题: “如果实用阶级是”邪恶的“那么这个问题就是一个稻草人的论点。 像我这样问:

“如果猪能飞,应该带伞吗?”

在上述问题中,我并不是说猪可以飞行,或者我同意他们可以飞行的主张。

典型的“xyz是邪恶的”陈述是修辞手段,旨在通过提出一个极端的观点来使你思考。 他们很less(如果有的话)作为文字事实的陈述。

实用程序类是有问题的,因为它们不能将支持它们的数据分成责任。

然而,它们是非常有用的,我一直把它们build造成永久性的结构,或者在更彻底的重构过程中作为垫脚石。

从清洁代码透视实用工具类违反单一责任和开放 – 封闭原则。 他们有很多改变的理由,并且在devise上是不可扩展的。 它们实际上只应该在重构过程中作为中间体来存在。

我猜想它开始变得邪恶的时候

1) 它变得太大 (在这种情况下,将它们分成有意义的类别)。
2) 存在不应该是静态方法的方法

但只要这些条件没有得到满足,我认为它们是非常有用的。

实用程序类是不好的,因为它们意味着你懒得想出一个更好的名字:)

这就是说,我很懒。 有时你只需要完成工作,你的思想是一片空白。当“工具”类开始爬行的时候,

将某个实用程序品牌化是非常容易的,因为devise者无法想到放置代码的适当位置。 通常很less有真正的“公用事业”。

作为一个经验法则,我通常将代码保存在首先使用的包中,然后只有在后来才真正需要其他地方时才重构为更通用的地方。 唯一的例外是,如果我已经有一个包执行相似/相关的function,并且代码最适合那里。

我并不完全同意实用阶级是邪恶的。

虽然公用事业类可能会以某种方式违反OO的原则,但并不总是不好的。

例如,想象一下你需要一个函数来清理一个匹配值x的所有子串的string。

stl c ++(截至目前)不直接支持这个。

你可以创build一个std :: string的多态扩展。

但问题是,你真的想要在项目中使用每个string作为扩展string类吗?

有时候面向对象不是真的有意义,而这是其中之一。 我们希望我们的程序与其他程序兼容,所以我们将坚持使用std :: string并创build一个类StringUtil_(或其他)。

如果你坚持每个类一个实用程序,我会说最好的。 我可以说为所有类或者一个类使用一个util都是愚蠢的。

包含无状态静态方法的工具类可能会有用。 这些通常很容易进行unit testing。

实用类是邪恶的,即使它们看起来非常有用和方便。 这篇文章解释了更多细节: http : //www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html如果你正在编写真正的面向对象的软件,你应该使用对象,不pipe他们有多less人会创造。

使用Java 8,您可以在接口中使用静态方法…问题已解决。

大多数Util类都不好,因为:

  1. 他们扩大了方法的范围。 他们使代码公开,否则将是私人的。 如果util方法是稳定的(即不需要更新),我认为复制和粘贴私有助手方法比把它暴露为API更好,并且更难理解组件的公共入口点是什么维护一个被称为层次结构的树状结构,每个方法都有一个父级方法,当您从多个父方法中调用方法时,这种方法更容易在精神上分离成更难的组件。
  2. 他们导致死代码。 随着时间的推移使用方法变得没有用,因为你的应用程序的演变,你最终没有使用的代码污染你的代码库。 如果它仍然是私有的,那么编译器会告诉你这个方法是没有用的,你可以把它移除(最好的代码根本就没有代码)。

有一些类比静态与dynamic库。

经验法则

你可以从两个angular度来看待这个问题:

  • 总的来说*Util方法往往是糟糕的代码devise或懒惰的命名约定的build议。
  • 这是可重复使用的跨域无状态function的合法devise解决scheme。 请注意,几乎所有的常见问题都有现有的解决scheme。

示例1.正确使用util类/模块。 外部库例子

假设您正在编写pipe理贷款和信用卡的申请。 这些模块的数据通过json格式的Web服务公开。 从理论上说,你可以手动将对象转换为jsonstring,但这会重新发明轮子。 正确的解决scheme是在两个模块中包含用于将java对象转换为所需格式的外部库。 (在例子中,我已经显示了gson )

在这里输入图像说明


示例2.正确使用util类/模块。 写你自己的util没有借口给其他团队成员

作为一个用例,假设我们需要在两个应用模块中进行一些计算,但是他们都需要知道什么时候在波兰有公共节假日。 从理论上讲,你可以在模块内进行这些计算,但是将这个function提取出来分开模块会更好。

这是小的,但重要的细节。 你写的类/模块不叫做HolidayUtil ,而是PolishHolidayCalculator 。 在function上它是一个util类,但是我们设法避免了通用词。

在这里输入图像说明

现在回头看这个问题,我会说C#扩展方法完全破坏了对实用类的需求。 但并不是所有的语言都有这样的天才构想。

你也有JavaScript,在那里你可以添加一个新的function到现有的对象。

但我不确定真的有一个优雅的方式来解决这个问题,像C ++这样的旧语言…

好的OO代码有点难写,很难find,因为写好的OO比编写体面的function代码需要更多的时间/知识。

而当你有预算的时候,你的老板并不总是高兴地看到你花了整整一天的时间写了一堆课。

当我不能添加一个方法到一个类(比如说, Account被Jr. Developers锁住了),我只需要添加一些静态方法到我的Utilities类中,像这样:

 public static int method01_Account(Object o, String... args) { Account acc = (Account)o; ... return acc.getInt(); }