公用事业班是邪恶的? [关闭]

问题描述 投票:80回答:14

我看到了这个帖子

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

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

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

design-patterns software-design
14个回答
109
投票

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

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

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


2
投票

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


1
投票

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


1
投票

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

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

静态与动态库有一些类比。


0
投票

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


-1
投票

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

Account

52
投票

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

  • 将静态实用程序方法设计为通用和可重用的。确保他们是无国籍的;即没有静态变量。
  • 如果您有许多实用程序方法,请将它们分区为类,以便开发人员可以轻松找到它们。
  • 不要使用实用程序类,其中域类中的静态或实例方法将是更好的解决方案。例如,考虑抽象基类中的方法或可实例化的帮助程序类是否是更好的解决方案。
  • 对于Java 8以上,接口中的“默认方法”可能是比实用程序类更好的选择。

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

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

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

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


12
投票

实用程序类是有问题的,因为它们无法将责任与支持它们的数据分组。

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

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


7
投票

我想它开始变得邪恶了

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

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


4
投票

经验法则

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

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

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

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

enter image description here


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

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

这是一个小而重要的细节。你写的类/模块不叫HolidayUtil,而是PolishHolidayCalculator。在功能上它是一个util类,但我们设法避免泛型词。

enter image description here


3
投票

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

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


3
投票

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

您还可以使用JavaScript,只需向现有对象添加新功能即可。

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

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

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


3
投票

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

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

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

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

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

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

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

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


2
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.