创建一个Catch-All AppToolbox类 - 这是一种不好的做法吗?

6

不确定应该放置类似以下函数的位置:

String PrettyPhone( String phoneNumber ) // return formatted (999) 999-9999
String EscapeInput( String inputString ) // gets rid of SQL-escapes like ' 

我为每个应用程序创建一个名为Toolbox的类,该类作为不适合放入其他类中的函数的存储库。我读到过这样的类是不好的编程实践,具体来说是不好的面向对象设计。然而,这些引用似乎更多是个别设计师和开发人员的意见,而不是普遍共识。因此,我的问题是,通用的工具箱是否是一种糟糕的设计模式?如果是,为什么?有什么替代方案吗?
8个回答

6
很好的问题。我发现任何足够复杂的项目都需要“实用”类。我认为这仅仅是因为面向对象编程的本质迫使我们将事物放在一个整齐结构化的层次分类中,而这并不总是可行或合适的(例如尝试为哺乳动物创建一个对象模型,然后将鸭嘴兽塞进去)。这就是促使面向方面编程(参见横切关注点)工作的问题。通常放入实用程序类的内容都是横切关注点。
使用工具箱或实用程序类的另一种方法是使用扩展方法为基元类型提供额外所需的功能。但是,对于它是否构成良好的软件设计,专家们意见不一。
我的最终建议是:如果需要,请使用它,只需确保您没有捷径更好的设计。当然,如果需要,您始终可以稍后进行重构。

2
在这些例子中,我更倾向于扩展字符串:
class PhoneNumber extends String
{
     public override string ToString()
     {
         // return (999) 999-9999
     }
}

如果你列出需要使用这些功能的所有位置,就可以确定实际使用它的位置,然后将其添加到适当的类中。这有时可能很困难,但仍然应该争取做到。
编辑:如下所示,您无法在C#中覆盖String。我要表达的重点是,这个操作是在电话号码上执行的,因此函数也属于电话号码类别。
interface PhoneNumber
{
    string Formatted();
}

如果您有不同的格式,可以交换PhoneNumber的实现,而无需在代码中使用if语句来处理,例如,
而不是:
if(country == Countries.UK) output = Toolbox.PhoneNumberUK(phoneNumber);
else ph = Toolbox.PhoneNumberUS(phoneNumber);

您可以直接使用以下方式:
output = phoneNumber.Formatted();

问题是关于C#,不是Java。无论如何,String类在.NET和Java中都是sealed(Java中的final)。 - Thomas Levesque

2
我认为静态帮助类是最容易想到的。 它是如此常见,以至于有些人甚至将其称为面向对象设计的一部分。 然而,帮助类的最大问题是它们往往变成一个大杂烩。 我在参与的一些较大项目中看到过这种情况。 当你正在处理一个类并不知道把这个和那个函数放在哪里时,你就会把它放在帮助类中。 在这一点上,您的帮助程序不能很好地传达它们的功能。 类名中的“helper”或“util”本身并没有什么意义。 我认为几乎所有OO专家都反对使用帮助程序,因为如果您足够思考,可以很容易地用更具描述性的类替换它们。 我倾向于支持这种方法,因为我认为帮助程序违反了单一责任原则。 说实话,这只是我的观点,仅供参考。 我对OOP有点固执 :)

我倾向于在一个通用库中创建特定的辅助函数。因此,您不会看到一个包含IO辅助函数和字符串辅助函数的通用辅助类。但是我确实有:IoHelper、StringHelper等等。这就是我如何实现我的“关注点分离”。;-) - Wim

1

这没有任何问题。一个要点是尝试将其分解为逻辑部分。通过这样做,您可以保持您的智能感知干净。

MyCore.Extensions.Formatting.People
MyCore.Extensions.Formatting.Xml
MyCore.Extensions.Formatting.Html

1

我的经验是实用函数很少单独出现。如果你需要一个格式化电话号码的方法,那么你也需要一个验证电话号码和解析电话号码的方法。遵循YAGNI原则,你肯定不想写这些东西,直到它们真正需要,但我认为将这样的功能分离成单独的类会很有帮助。这些类从单个方法发展成小型子系统的增长将自然而然地随着时间的推移发生。我发现这是保持代码组织、可理解和可维护性最简单的方法。


1
当我创建一个应用程序时,我通常会创建一个静态类,其中包含静态方法和属性,我无法确定在哪里放置它们。
这不是特别好的设计,但这就是重点:它给了我一个地方来本地化一整个设计决策类,我还没有想清楚。通常随着应用程序的增长和通过重构进行的改进,这些方法和属性实际上应该驻留的位置变得更加清晰。令人欣慰的是,重构工具的状态通常不需要太多痛苦就可以进行这些更改。
我尝试过另一种方式,但另一种方式基本上是在我足够了解我的应用程序以正确设计对象模型之前实现对象模型。如果我这样做,我会花费相当多的时间和精力来想出一个平庸的解决方案,而我必须在将来某个时候重新审视并从头开始重建。好吧,如果我知道我将重构这段代码,那么我跳过设计和构建不必要复杂的类的步骤怎么样?
例如,我已经构建了一个被多个客户使用的应用程序。我很早就意识到需要一种方法来区分需要为不同客户提供不同服务的方法。我构建了一个静态实用方法,可以在程序中任何需要调用自定义方法的地方调用它,并将其放置在我的静态类中。
这个方法运行了几个月,但是有一天,它开始变得丑陋不堪。因此,我决定将其重构为自己的类。当我查看所有调用该方法的代码时,非常清楚,所有自定义方法都需要成为抽象类的成员,客户的程序集需要包含一个派生类,该类实现所有抽象方法,然后程序只需要从配置中获取程序集和命名空间的名称,并在启动时创建自定义功能类的实例。对于我来说,找到所有必须自定义的方法非常简单,因为我只需要找到每个调用我的加载自定义功能方法的地方。花费了我大部分下午时间来审查整个代码库并合理化这个设计,最终结果非常灵活、强大,解决了正确的问题。
事实上,当我第一次实现那个方法(实际上是三个或四个相关的方法)时,我意识到它不是正确的答案。但我不知道足够的知识来决定正确的答案是什么。因此,我选择了最简单的错误答案,直到正确的答案变得清晰明了。

0

我认为被人们不赞同的原因是因为“工具箱”可能会变得越来越大,每次调用单个函数时都需要加载大量资源。

将适用于对象的方法放在实际类中也更加优雅 - 这样更有意义。

话虽如此,我个人认为这并不是问题,但出于上述原因,我会避免使用它。


0

我发了一条评论,但想再详细解释一下。

我的做法是创建一个带有命名空间的通用库:[组织].[产品].Common 作为根目录,以及一个子命名空间 Helpers。

这里有几个人提到创建一个类,并将一些不知道放在哪里的东西塞进去。这是错误的。我会说,即使你只需要一个帮助方法,它也与某些东西相关,因此创建一个适当命名的(IoHelper、StringHelper 等)静态帮助类,并将其放在 Helpers 命名空间中。这样,你就可以得到一些结构和一定程度的关注点分离。

在根命名空间中,你可以使用需要状态的实例工具类(它们存在!)。当然,也要使用适当的类名,但不要加上 Helper 后缀。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接