问题 实用课程..好还是坏?


我一直在阅读通过在代码中使用静态类/单例来创建依赖项,是不好的形式,并产生问题,即。紧耦合和单元测试。

我有一种情况,我有一组url解析方法没有与它们相关的状态,并只使用方法的输入参数执行操作。我相信你熟悉这种方法。

在过去,我会继续创建一个类并添加这些方法,并直接从我的代码中调用它们,例如。

UrlParser.ParseUrl(url);

但是等一下,就是将依赖引入另一个类。我不确定这些“实用”类是否是坏的,因为它们是无状态的,这最小化了所述静态类和单例的一些问题。有人可以澄清一下吗?

我应该将方法移动到调用类,即只有调用类将使用该方法。这可能违反了“单一责任原则”。


11520
2017-11-04 01:48


起源

“我有一种情况,我有一组url解析方法,没有与它们相关的状态,只使用方法的输入参数执行操作。我相信你熟悉这种方法。”这种方法称为纯函数 - morphles
查看这篇博文(主要谈论Java,但在这里仍然相关): yegor256.com/2014/05/05/oop-alternative-to-utility-classes.html - yegor256


答案:


从理论设计的角度来看,我觉得在可能的情况下,实用工具类是可以避免的。它们基本上与静态类没什么不同(虽然稍微好一点,因为它们没有状态)。

然而,从实际的角度来看,我确实创造了这些,并鼓励他们使用 在适当的时候。试图避免实用程序类通常很麻烦,并导致代码维护较少。但是,我确实鼓励我的开发人员在可能的情况下在公共API中避免这些。

例如,在您的情况下,我觉得UrlParser.ParseUrl(...)可能更好地作为一个类处理。看着 的System.Uri 在BCL中 - 这为统一资源标识符提供了一个干净,易于使用的界面,该界面运行良好,并保持实际状态。我更喜欢这种方法来处理一个对字符串起作用的实用程序方法,并强制用户传递一个字符串,记得验证它,等等。


7
2017-11-04 01:55



好点子。 System.Uri是将这样的静态实用程序方法转换为对象的一个​​很好的例子。 - Jamie Penney
鉴于他们的例子,这也与他们的具体问题非常相关;) - Reed Copsey
一个非常糟糕的例子,因为已知System.Uri存在与原始字符串表示和与绝对相对路径的转换相关的问题。这就是每个抽象设计和API公式上都会发生的事情,你从来没有开箱即用,而且对于MSFT框架设计人员而言,它需要很长时间才能正确完成并且通常会导致比使用简单且已知的静态方法更复杂的问题。具有实用性。但这通常是“框架”和“设计指南”狂热方法的问题。 - rama-jka toti
有人可以解释一下这是什么意思“他们基本上没有静态类别(虽然稍微好一点,因为他们没有国家)。”也许我错过了如何声明“实用程序”类。根据示例,它看起来像一个静态类。里德指的是什么“国家”? - Michael Petito
它通常只是一个没有字段或属性的静态类,因此没有保存的“状态”,而只是一系列用作实用程序的方法。 - Reed Copsey


实用程序类是可以的.....只要它们不违反设计原则。在使用核心框架类时,请愉快地使用它们。

这些类应该有良好的命名和逻辑。实际上,它们不是“实用”,而是本土课程不提供的新兴框架的一部分。

使用诸如扩展方法之类的东西也很有用,可以将功能与“正确”类对齐。但是,它们可能会引起一些混淆,因为扩展程序没有与它们通常扩展的类一起打包,这不是理想的,但仍然可以非常有用并产生更清晰的代码。


2
2017-11-04 01:53





您总是可以创建一个接口,并使用依赖注入与实现该接口而不是静态类的类的实例。

问题变成了,真的值得努力吗?在某些系统中,答案是肯定的,但在其他系统中,特别是较小的答案,答案可能是否定的。


2
2017-11-04 01:54



这也是我的建议。如果实用方法复杂或计算成本高,我建议使用依赖注入将其作为某种服务对象注入。否则我不担心。 - Jamie Penney
注入不会消除依赖性。它只是使它更容易处理。 - OscarRyz
唯一的方法 去掉 依赖是删除它。我不明白你的观点。 - Matthew Scharley


我同意这里的一些其他回应,它是经典的单例,它维护一个有状态对象的单个实例,这个实例是要避免的,而不一定是没有状态恶意的实用类。我也同意里德的意见,如果可能的话,将这些实用程序方法放在一个有意义的类中,并且逻辑上怀疑这样的方法会存在。我想补充一点,通常这些静态实用程序方法可能是扩展方法的良好候选者。


1
2017-11-04 01:59





我真的,真的试图避开它们,但是我们在开玩笑......它们会蔓延到每个系统中。然而,在给出的示例中,我将使用URL对象,然后该URL对象将公开URL的各种属性(协议,域,路径和查询字符串参数)。 几乎 每次我想创建静态实用程序类时,我都可以通过创建执行此类工作的对象来获得更多价值。

以类似的方式,我创建了许多自定义控件,这些控件已经内置了百分比,货币,电话号码等内容的验证。在这之前,我有一个Parser实用程序类,它具有所有这些规则,但它使得在已经知道基本规则的页面上删除控件变得更加清晰(因此只需要 商业逻辑 要添加的验证)。

我仍然保留解析器实用程序类和这些控件 隐藏 那个静态类,但是广泛使用它(将所有解析保存在一个容易找到的地方)。在这方面,我认为使用实用程序类是可以接受的,因为它允许我应用“不要重复自己”,而我通过控件或使用实用程序的其他对象获得实例类的好处。


1
2017-11-04 01:59





这实际上取决于背景以及我们如何使用它。

实用类本身也不错。但是,如果我们以糟糕的方式使用它会变得很糟糕。每个设计模式(特别是Singleton模式)都可以很容易地变成反模式,对于Utility类也是如此。

在软件设计中,我们需要在灵活性和简单性之间取得平衡。如果我们要创建一个只负责字符串操作的StringUtils:

  • 它违反了SRP(单一责任原则)吗? - >不,这是开发人员对违反SRP的实用程序类承担过多责任。
  • “它不能使用DI框架注入” - > StringUtils实现会有所不同吗?我们要在运行时切换它的实现吗?我们要嘲笑它吗?当然不是。

=>公用事业课,他们都不错。这是开发人员的错,使其变坏。

这一切都取决于具体情况。如果您要创建一个仅包含单一职责的实用程序类,并且仅在模块或层中私有使用。然后你仍然很好。


1
2017-10-30 17:19





只要你设计好它们就很好(也就是说,你不必经常改变它们的签名)。

这些实用方法通常不会改变,因为它们只做一件事。当你想要将更复杂的对象紧密到另一个时,问题就来了。如果其中一个需要更换或更换,如果你有高度耦合,那将更难。

由于这些实用方法不会改变,我常说这不是什么大问题。

我认为如果一遍又一遍地复制/粘贴相同的实用方法,那将是最糟糕的。

这个视频 如何设计一个好的API及其重要性作者Joshua Bloch解释了设计API时要记住的几个概念(这将是您的实用程序库)。虽然他是公认的Java架构师,但内容适用于所有编程语言。


0
2017-11-04 01:53





谨慎使用它们,您希望尽可能多地将逻辑放入类中,这样它们就不会成为数据容器。

但是,与此同时,你无法真正避免使用它们,有时它们是必需的。

在这种情况下,我认为这没关系。

仅供参考,有一个system.web.httputility类,它包含许多常见的http实用程序,您可能会觉得它们很有用。


0
2017-11-04 02:04