公用事业班是邪恶的?

时间:2010-07-27 00:55:11

标签: design-patterns software-design

我看到了这个帖子

If a "Utilities" class is evil, where do I put my generic code?

并想到为什么实用类邪恶?

假设我有一个很多类的域模型。我需要能够xml-ify实例。我是否在父级上创建了一个toXml方法?我是否制作了MyDomainXmlUtility.toXml助手类?这是业务需求跨越整个域模型的情况 - 它真的属于实例方法吗?如果应用程序的xml功能上有一堆辅助方法呢?

14 个答案:

答案 0 :(得分:104)

实用程序类并不完全是邪恶的,但它们可能违反构成良好的面向对象设计的原则。在良好的面向对象设计中,大多数类应该代表单个事物及其所有属性和操作。如果您正在操作某个东西,那么该方法应该是该东西的成员。

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

实际上,您需要做的是考虑您的设计并确定最适合放置方法的位置。通常,它是一个类内的操作。但是,有时,它确实是一个实用类。但是,当您使用实用程序类时,不要只是将随机方法放入其中,而是按目的和功能组织方法。

答案 1 :(得分:47)

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

  • 将静态实用程序方法设计为通用和可重用的。确保他们是无国籍的;即没有静态变量。

  • 如果你有很多实用工具方法,可以让它们以一种让开发人员更容易找到它们的方式将它们分成类。

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

  • 对于Java 8以上版本,界面中的“默认方法”可能是比实用程序类更好的选择。


另一种看待这个问题的方法是观察在引用的问题中,“如果实用程序类是”邪恶的“”是一个稻草人的论点。就像我一样问:

  

“如果猪可以飞,我应该带伞吗?”

在上面的问题中,我实际上并不是说猪可以飞......或者我同意他们可以飞行的命题。

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

答案 2 :(得分:12)

实用程序类存在问题,因为它们无法使用支持它们的数据对责任进行分组。

然而,它们非常有用,我将它们作为永久性结构或在更彻底的重构中作为垫脚石一直构建。

Clean Code角度来看,实用程序类违反了单一责任和开放原则。他们有很多改变的理由,而且设计不可扩展。它们实际上只应该在重构过程中作为中间残余存在。

答案 3 :(得分:7)

我认为

时它开始变得邪恶

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

但只要不满足这些条件,我认为它们非常有用。

答案 4 :(得分:4)

经验法则

您可以从两个角度来看待这个问题:

  • 整体*Util方法通常是错误代码设计或惰性命名约定的建议。
  • 它是可重用的跨域无状态功能的合法设计解决方案。请注意,对于几乎所有常见问题,都有现有的解决方案。

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

假设您正在编写管理贷款和信用卡的应用程序。来自这些模块的数据通过json格式的Web服务公开。 理论上,您可以手动将对象转换为json中的字符串,但这会重新发明轮子。正确的解决方案是在两个模块中包含用于将java对象转换为所需格式的外部库。 (在示例图片中,我显示了gson

enter image description here

示例2.正确使用util类/模块。编写自己的util,无需借口给其他团队成员

作为一个用例假设我们需要在两个应用模块中执行一些计算,但他们都需要知道波兰何时有公共假期。从理论上讲,您可以在模块内部进行这些计算,但最好将此功能提取到单独的模块中。

这是一个小而重要的细节。您编写的类/模块不是HolidayUtil,而是PolishHolidayCalculator。从功能上讲,它是util类,但我们设法避免使用通用词。

enter image description here

答案 5 :(得分:3)

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

话虽这么说,我很懒。有时候你只需要完成工作而你的思想是空白的......那就是“实用工具”课程开始悄悄进入的时候。

答案 6 :(得分:3)

我并不完全同意公用事业类是邪恶的。

虽然实用程序类可能在某些方面违反OO主体,但它们并不总是坏事。

例如,假设您需要一个函数来清除与值x匹配的所有子字符串的字符串。

stl c ++(截至目前)不直接支持此功能。

您可以创建std::string的多态扩展名。

但问题是,你真的希望你在项目中使用的每个字符串都是你的扩展字符串类吗?

有时OO没有意义,这就是其中之一。我们希望我们的程序与其他程序兼容,因此我们将坚持std::string并创建一个类StringUtil_(或其他东西)。

如果你坚持每班一个工具,我会说最好。我会说,为所有类创建一个util或者为一个类创建多个util是很愚蠢的。

答案 7 :(得分:3)

现在回顾这个问题,我会说C#扩展方法完全破坏了对实用程序类的需求。但并非所有语言都有这种天才的结构。

您还拥有JavaScript,您可以在其中向现有对象添加新功能。

但是我不确定是否有一种优雅的方法可以用像C ++这样的旧语言解决这个问题......

良好的OO代码有点难以编写,并且很难找到,因为编写Good OO需要比编写合适的功能代码更多的时间/知识。

当你达到预算时,你的老板并不总是很高兴看到你花了一整天的时间写一堆课......

答案 8 :(得分:2)

仅仅因为设计师无法想到放置代码的合适位置,所以很容易将实用程序称为实用程序。通常很少有真正的“实用工具”。

根据经验,我通常会将代码保存在首次使用它的包中,然后只有重构到更通用的地方,如果我发现稍后其他地方确实需要它。唯一的例外是如果我已经有一个执行类似/相关功能的包,并且代码最适合那里。

答案 9 :(得分:2)

包含无状态静态方法的实用程序类可能很有用。这些通常很容易进行单元测试。

答案 10 :(得分:1)

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

答案 11 :(得分:1)

大多数Util类都很糟糕,因为:

  1. 他们扩大了方法的范围。他们使代码公开,否则将是私有的。如果多个调用者在不同的类中需要util方法并且稳定(即不需要更新),我认为将私有帮助器方法复制并粘贴到调用类中会更好。一旦将它作为API公开,就会更难理解jar组件的公共入口点是什么(维护一个名为层次结构的树结构,每个方法有一个父级。这更容易在心理上分离成更难的组件你有从多个父方法调用的方法)。
  2. 它们导致死代码。随着应用程序的发展,随着时间的推移,随着时间的推移,未使用的代码库会导致未使用的代码污染您的代码库。如果它保持私有,你的编译器会告诉你方法是未使用的,你可以删除它(最好的代码根本就没有代码)。一旦你将这种方法设为非私有,你的计算机将无力帮助你删除未使用的代码。可以从所有计算机都知道的不同jar文件中调用它。
  3. 静态与动态库有一些类比。

答案 12 :(得分:0)

实用程序类并不总是邪恶的。但是它们应该只包含各种功能中常见的方法。如果有些方法只能在有限数量的类中使用,请考虑将抽象类创建为公共父类并将方法放入其中。

答案 13 :(得分:-1)

当我无法向类中添加方法时(例如,Account被Jr. Developers的更改锁定),我只是向我的Utilities类添加一些静态方法,如下所示:

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