2009-11-04 93 views
11

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

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

在过去,我会着手创建一个类,并添加这些方法,直接从我的代码如给他们打电话。

UrlParser.ParseUrl(url); 

但是等一下,那就是引入了对另一个类的依赖。我不确定这些'效用'类是否是坏的,因为它们是无状态的,并且这最大限度地减少了所述静态类和单例的一些问题。有人能澄清这一点吗?

我应该移动的方法来调用类,即如果只是调用的类将使用的方法。这可能违反“单一责任原则”。

+0

“我有一种情况,我有一组url解析方法,它们没有与它们相关联的状态,并且只使用方法的输入参数执行操作,我相信你很熟悉这种方法。这种方法将被称为纯函数 – morphles 2011-05-02 09:51:37

+0

检查此博客文章(主要谈论Java,但仍然在这里相关):http://www.yegor256.com/2014/05/05/oop-alternative-to-utility-classes。 html – yegor256 2014-05-06 11:13:14

回答

7

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

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

例如,你的情况,我觉得UrlParser.ParseUrl(...)可能更好地处理为一个类。查看BCL中的System.Uri - 这为处理统一资源标识符处理干净,易于使用的界面,运行良好,并保持实际状态。我更喜欢这种方法来处理字符串的实用方法,并强制用户绕过字符串,记住验证它,等等。

+0

好点。 System.Uri是将这种静态工具方法转换为对象的一个​​很好的例子。 – 2009-11-04 01:58:03

+0

这与他们的具体问题也是非常相关的,因为他们的例子;) – 2009-11-04 01:58:56

+0

一个非常糟糕的例子,因为System.Uri被认为是与原始字符串表示和从绝对相对路径转换有关的问题。这就是每个抽象设计和API制定过程中发生的事情,你永远无法从盒子中得到它,并且使用MSFT框架设计师需要很长的时间才能正确地使用它,并且常常导致比静态方法更容易并且已知的复杂性本质上是功能性的。但是,这是'框架'和'设计指南'狂热的方法总的问题。“ – 2009-11-04 09:22:57

0

他们都很好,只要你设计得很好(也就是说,你没有自己的签名,从随着时间而改变)。

这些实用方法不会经常改变,因为它们只做一件事。问题出现在您想要将更复杂的对象紧固到另一个对象时。如果他们中的一个需要改变或被替换,那么如果你让他们高度耦合,将会更困难。

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

我认为如果您一遍又一遍地复制/粘贴相同的实用程序方法,情况会更糟糕。

这由Joshua布洛赫视频How to design a good API and why it matters,解释几个概念设计API(这将是您的工具库)时,要牢记。虽然他是公认的Java架构师,但内容适用于所有编程语言。

2

工具类没问题,只要他们不违反设计原则。像使用核心框架类一样愉快地使用它们。

这些类应该命名合理。真的,他们并不是那么“实用”,而是本土课程所没有提供的新兴实体的一部分。

使用类似扩展方法的东西也可以用于将功能对齐到“正确”类。但是,它们可能会造成一些混淆,因为扩展没有与它们通常扩展的类一起打包,这并不理想,但仍然可以非常有用并且生成更简洁的代码。

2

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

问题变成真的值得吗?在一些系统中,答案是肯定的,但在其他系统中,尤其是较小的答案中,答案可能不是。

+0

这也是我的建议。如果实用方法复杂或计算成本高昂,我会建议使用依赖注入将其作为某种服务对象注入。否则我不会担心。 – 2009-11-04 01:56:39

+0

注入不会消除依赖关系。它只是让它更容易处理。 – OscarRyz 2009-11-04 01:58:48

+0

删除**依赖关系的唯一方法是删除它。我没有看到你的观点。 – 2009-11-04 02:00:48

1

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

以类似的方式,我创建了很多自定义控件,内置了百分比,货币,电话号码等的验证。在此之前,我有一个解析器实用程序类,它具有所有这些规则,但它使得它更加简洁,只需将控件放在已知基本规则的页面上(因此只需要业务逻辑验证即可添加)。

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

1

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

0

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

但是,在同一时间你不能真正避免utilites,他们有时是必需的。

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

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

1

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

实用程序类本身并不差。但是,如果我们以不好的方式使用它会变得糟糕。每种设计模式(尤其是Singleton模式)都可以很容易地变成反模式,对于Utility类也是如此。

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

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

=>实用程序类themselve并不差。这是开发商的错,使它变得糟糕。

这一切都取决于上下文。如果你只是想创建一个只包含单一责任的实用程序类,并且只在模块或图层中私下使用。那么你对它仍然很好。