是否有“实用”类值得关注?[关闭]


14

我有时会创建“ Util”类,这些类主要用于保存似乎并不真正属于其他地方的方法和值。但是每次创建这些类之一时,我都会想:“哦,哦,我以后会后悔的……”,因为我在某处读到不好。

但另一方面,似乎有两个令人信服的案例(至少对我而言):

  1. 包中多个类中使用的实现机密
  2. 提供有用的功能来扩展类,而不会使其界面混乱

我要毁灭了吗?你说的话 !!我应该重构吗?


11
停止Util在您的班级名称中使用。问题解决了。
yannis 2012年

3
@MattFenwick Yannis很好。给一个类命名SomethingUtil有点懒惰,只是掩盖了该类的真正目的-与名为SomethingManager或类的类相同SomethingService。如果该班级只有一个职责,那么给它起一个有意义的名字应该很容易。如果不是,那是要解决的真正问题……
MattDavey 2012年

1
@MDavey好点,它可能是一个错误的单词选择Util,尽管显然我没想

1
如果您创建多个* Util类,并由它们操作的对象分开,那么我认为这很好。我没有遇到StringUtil,ListUtil等任何问题。除非您使用的是C#,否则应使用扩展方法。
marco-fiset 2012年

4
我经常有一些用于相关目的的功能。他们不能很好地映射到一个类。我不需要ImageResizer类,我只需要一个ResizeImage函数。因此,我将相关函数放入静态类(如ImageTools)中。对于尚未进行任何分组的函数,我确实有一个Util静态类来保存它们,但是一旦我有几个相互之间足够相关以适合一个类的函数,就将它们移动。我看不出有更好的面向对象的方法来处理此问题。
菲利普(Philip)

Answers:


26

现代OO设计接受并非所有事物都是对象。有些事情是行为或公式,而有些则没有状态。最好将这些事物建模为纯函数以从该设计中受益。

Java和C#(及其他)和Java要求您创建一个util类,并跳过该箍以完成此操作。烦人,但世界末日却没有;从设计的角度来看并不是很麻烦。


是的,这就是我的想法。感谢您的回复!

同意,尽管应该提到,.NET现在提供扩展方法和部分类作为此方法的替代方法。如果将此项目推向极致,那么您最终会使用Ruby mixins-一种对实用程序类几乎不需要的语言。
neontapir 2012年

2
@neontapir-除了扩展方法的实现,基本上会迫使您使用扩展方法来制作util类……
Telastyn 2012年

真正。Util类在两个地方受阻,一个地方在类本身中,另一个地方在呼叫现场。通过使方法看起来像它们存在于增强对象上,扩展方法可以解决呼叫站点问题。我同意,仍然存在Util类存在的问题。
neontapir 2012年

16

永不说永不”

我不认为这一定是坏的,只有当您不好地做它并滥用它时,它才是坏的。

我们都需要工具和实用程序

首先,我们所有人都使用一些有时被认为是无处不在且必须具备的库。例如,在Java世界中,Google Guava或某些Apache CommonsApache Commons LangApache Commons Collections等)。

因此,显然需要这些。

避免硬语,重复和引入错误

如果你觉得这些都非常简单,只是一个非常大的一堆这些的Util类,您形容,除了有人通过竭尽全力去得到他们(相对)的权利,他们一直时间 - 测试和其他严重眼揉成团。

因此,当我想编写一个Util类的想法时,我想说的第一个经验法则是检查Util该类是否确实不存在。

我看到的唯一反论点是当您想限制依赖项时,因为:

  • 您想限制依赖项的内存占用量,
  • 或者您想严格控制开发人员的使用权限(在大型团队中会发生这种情况,或者某个特定的框架因拥有奇特的超级烂类而绝对避免在某个地方使用)。

但这两种情况都可以通过使用ProGuard或等效工具重新打包lib 或自己拆解来解决(对于Maven用户,maven-shade-plugin提供了一些过滤模式以将其集成为构建的一部分)。

因此,如果它在一个库中并且与您的用例匹配,并且没有基准告诉您,请使用它。如果它与您的内容有所不同,请对其进行扩展(如果可能)或进行扩展,或者在万不得已时将其重写。

命名约定

但是,到目前为止,在这个答案中,我称他们Util像您一样。不要这样命名。

给他们起有意义的名字。以Google Guava为例(非常非常好),然后想象一下,com.google.guava命名空间实际上就是您的util根。

util在最坏的情况下调用您的包,但不要调用类。如果处理String对象和字符串构造的操作,则将其命名为Strings,而不是StringUtils(抱歉,Apache Commons Lang-我仍然喜欢并使用您!)。如果它做了特定的事情,请选择一个特定的类名(如SplitterJoiner)。

单元测试

如果必须使用这些实用程序,请确保对其进行单元测试。关于实用程序的好处是,它们通常是相当独立的组件,它们接受特定的输入并返回特定的输出。这就是概念。因此,没有理由不对它们进行单元测试。

此外,单元测试将允许您定义和记录其API合同。如果测试失败,则可能是您以错误的方式进行了某些更改,或者这意味着您正在尝试更改API的合同(或者您的原始测试已被废除,请从中吸取教训,不要再做)

API设计

您可能需要很长一段时间来遵循这些API的设计决策。因此,尽管不花很多时间在编写Splitter-clone上,但请谨慎对待问题。

问自己几个问题:

  • 如果您的实用程序方法成为一组类似有用的方法的一部分,那么它是否可以单独保证一个类,还是静态方法足够好?
  • 您是否需要工厂方法来创建对象并提高API的可读性?
  • 说到可读性,您需要Fluent APIbuilders等吗?

您希望这些实用程序涵盖大量用例,健壮,稳定,有据可查,遵循最少惊讶的原则并且具有独立性。理想情况下,您的utils的每个子软件包,或至少整个util软件包,都应可导出到捆绑软件中,以便于重复使用。

和往常一样,在这里向巨人学习:

是的,其中许多都强调集合和数据结构,但是请不要告诉我,通常您不是直接或间接实现大多数utils的地方或目的。


+1 forIf deals with String objects and manipulation of string constructs, call it Strings, not StringUtils
TulainsCórdova'12

+1对于.Net中的API设计,请阅读.Net架构师的书。框架设计指南:可重用.NET库的约定,惯用语和模式
MarkJ 2012年

为什么将其称为Strings而不是StringUtil?对我来说,字符串听起来像一个集合对象。
bdrx 2014年

@bdrx:对我来说,StringsCollectionTypeHere如果您要具体实现,则可以是一个集合对象。如果这些字符串在您的应用程序上下文中具有特定含义,则可以使用更具体的名称。对于这种特殊情况,Guava使用Strings了与字符串相关的帮助程序,而不是StringUtilsCommons Lang中。我发现它是完全可以接受的,对我而言,这仅意味着类可以处理String对象,或者是用于管理字符串的通用类。
haylem 2014年

@bdrx:一般来说NameUtils,我会无休止地困扰我,因为如果它位于带有明显标签的程序包名称下,我就已经知道它是一个实用程序类(如果没有,则可以通过查看API迅速找出来)。当人们将东西声明为,或时SomethingInterface,这让我很烦。当使用C而不是使用IDE进行编码时,我对这样的伪像很满意。今天,我通常不需要这些东西。ISomethingSomethingImpl
haylem 2014年

8

实用程序类不是具有凝聚力的,并且通常是不良的设计,因为类必须具有更改的单一原因(单一责任原则)。

不过,我已经看到了“UTIL”非常的Java API的类,如:MathCollectionsArrays

实际上,它们是实用程序类,但它们的所有方法都与单个主题相关,一个具有数学运算,一个具有用于操纵集合的方法,另一个具有用于操纵数组的方法。

尽量不要在实用程序类中有完全不相关的方法。如果是这样,您也可以将它们放到它们真正属于的位置。

如果必须有util的类,然后试戴按主题进行分离像Java的MathCollectionsArrays。至少,即使它们只是名称空间,也显示出一些设计意图。

首先,我总是避免使用实用程序类,并且从来不需要创建一个。


感谢您的回复。因此,即使util类是程序包私有的,它们仍然具有设计气味吗?

并非所有事物都属于一个类。如果您坚持使用坚持要这样做的语言,那么将发生utils类。
jk。

1
好的,实用程序类没有一个设计原理来让您知道有多少方法太多,因为一开始没有内聚性。你怎么会知道停下来?30种方法之后?40?100?另一方面,OOP原理让您了解方法何时不属于特定类,以及该类何时执行过多操作。这就是所谓的凝聚力。但是正如我在回答中告诉您的那样,设计Java的人正是使用实用程序类。你也做不到 我不创建实用程序类,也没有遇到某些不属于普通类的情况。
图兰斯·科尔多瓦

1
实用程序类通常不是类,它们只是包含一堆通常是纯函数的名称空间。纯函数具有尽可能的内聚性。
jk。

1
@jk我的意思是Utils ...顺便提一个类似MathArrays表示至少某种设计意图的名称。
图兰斯·科尔多瓦

3

尽管我更喜欢util类,但完全可以接受utilClassNameHelper。.NET BCL甚至包含帮助程序类。要记住的最重要的事情是,彻底记录类的用途以及每个单独的辅助方法,并使其成为高质量的可维护代码。

并且不要被助手类迷住了。


0

我使用两层方法。“ util”包(文件夹)中的“ Globals”类。为了使某些东西进入“ Globals”类或“ util”包,它必须是:

  1. 简单,
  2. 不变,
  3. 不依赖您的应用程序中的任何其他内容

通过这些测试的示例:

  • 系统范围的日期和十进制格式
  • 不变的全局数据,例如EARLIEST_YEAR(系统支持)或IS_TEST_MODE或诸如此类的真正的全局且不变(在系统运行时)的值。
  • 很小的帮助程序方法,可以解决您的语言,框架或工具包中的空白

这是一个非常小的助手方法的示例,它完全独立于应用程序的其余部分:

public static String ordinal(final int i) {
    return (i == 1) ? "1st" :
           (i == 2) ? "2nd" :
           (i == 3) ? "3rd" :
           new StringBuilder().append(i).append("th").toString();
}

看着这个,我可以看到一个错误,那就是21应该是“ 21st”,22应该是“ 22nd”,依此类推,但这并不重要。

如果这些辅助方法之一增长或变得复杂,则应将其移至util包中的自己的类。如果util程序包中的两个或更多帮助程序类相互关联,则应将它们移到自己的程序包中。如果事实证明常量或辅助方法与您的应用程序的特定部分相关,则应将其移到那里。

您应该能够证明为什么在Globals类或util包中粘贴的所有内容没有比这更好的地方了,并且应该使用上述测试定期对其进行清理。否则,您将造成混乱。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.