14

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

我有一种情况,我有一组没有与之关联的状态的 url 解析方法,并且仅使用该方法的输入参数执行操作。我相信你对这种方法很熟悉。

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

UrlParser.ParseUrl(url);

但是等一下,这是对另一个类的依赖。我不确定这些“实用程序”类是否不好,因为它们是无状态的,这最大限度地减少了所述静态类和单例的一些问题。有人可以澄清一下吗?

我是否应该将方法移动到调用类,也就是说,如果只有调用类将使用该方法。这可能违反“单一职责原则”。

4

8 回答 8

9

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

然而,从实际的角度来看,我确实创建了这些,并鼓励在适当的时候使用它们。试图避免使用实用程序类通常很麻烦,并且会导致代码的可维护性降低。但是,我确实尝试鼓励我的开发人员尽可能避免在公共 API 中使用这些。

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

于 2009-11-04T01:55:08.877 回答
2

实用程序类是好的.....只要它们不违反设计原则。像使用核心框架类一样愉快地使用它们。

这些类应该命名良好且合乎逻辑。实际上,它们并不是太多的“实用程序”,而是本地类不提供的新兴框架的一部分。

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

于 2009-11-04T01:53:36.197 回答
2

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

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

于 2009-11-04T01:54:25.237 回答
1

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

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

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

于 2009-11-04T01:59:02.603 回答
1

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

于 2009-11-04T01:59:29.173 回答
1

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

实用程序类本身还不错。但是,如果我们以不好的方式使用它,它会变得很糟糕。每个设计模式(尤其是单例模式)都可以很容易地变成反模式,实用程序类也是如此。

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

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

=> 实用程序类本身也不错。是开发人员的错使它变得糟糕。

这一切都取决于上下文。如果您只是要创建一个仅包含单一职责的实用程序类,并且仅在模块或层内私下使用。那你还是很好的。

于 2016-10-30T17:19:56.923 回答
0

只要您设计好它们就可以了(也就是说,您不必不时更改它们的签名)。

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

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

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

Joshua Bloch 的这个视频How to design a good API and Why it Matter 解释了设计 API(这将是您的实用程序库)时要牢记的几个概念。尽管他是公认的 Java 架构师,但内容适用于所有编程语言。

于 2009-11-04T01:53:10.010 回答
0

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

但是,与此同时,您也不能真正避免使用实用程序,它们有时是必需的。

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

仅供参考,有 system.web.httputility 类,其中包含许多您可能会发现有用的常见 http 实用程序。

于 2009-11-04T02:04:49.473 回答