永远不知道在哪里放置以下功能:
String PrettyPhone( String phoneNumber ) // return formatted (999) 999-9999
String EscapeInput( String inputString ) // gets rid of SQL-escapes like '
我为每个应用程序创建一个Toolbox
类,作为不完全适合另一个类的函数的存储库。我已经读过这样的类是糟糕的编程实践,特别是糟糕的面向对象设计。然而,所述参考文献似乎更多的是个人设计师和开发人员的意见,而不是一个总体上的共识。所以我的问题是,一个无所不能的工具箱是一个糟糕的设计模式?如果是这样,为什么,还有什么替代方案?
答案 0 :(得分:6)
好问题。我总是发现任何足够复杂的项目都需要“实用”类。我认为这仅仅是因为面向对象编程的本质迫使我们将事物置于一个整齐的结构化分层分类中,而这并不总是可行或适当的(例如,尝试为哺乳动物创建一个对象模型,然后挤压鸭嘴兽) )。这是促使工作进入aspect oriented programming(c.f。cross cutting concern)的问题。通常,实用类中的内容是跨领域的问题。
使用工具箱或实用程序类的一种替代方法是使用扩展方法为基本类型提供其他所需的功能。但是,陪审团仍然不清楚这是否构成了良好的软件设计。
关于这个问题我的最后一句话是:如果你需要,可以选择它,只要确保你没有做出更好的设计。当然,如果需要,您可以随时重构。
答案 1 :(得分:2)
在这些例子中,我更倾向于扩展String:
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();
答案 2 :(得分:2)
我认为静态助手类是首先想到的。有些人甚至将它称为面向对象设计的一部分,这是很常见的。但是,帮助程序类的最大问题是它们倾向于成为大型转储。我想我在一些较大的项目中看到了这种情况。你正在上课,不知道在哪里坚持这个和那个功能所以你把它放在你的帮助班里。在这一点上,你的帮助者不能很好地沟通他们所做的事情。类名中的'helper'或'util'本身并不意味着什么。我认为几乎所有OO专家都反对帮助者,因为如果你给予足够的思考,你可以很容易地用更具描述性的类替换它们。我倾向于同意这种方法,因为我认为帮助者违反了单一责任原则。老实说,带上一粒盐。我对OOP有点看法:)
答案 3 :(得分:1)
这没有错。有一件事是尝试将其分解为逻辑部分。通过这样做,您可以保持智能感知。
MyCore.Extensions.Formatting.People
MyCore.Extensions.Formatting.Xml
MyCore.Extensions.Formatting.Html
答案 4 :(得分:1)
我的经验是,实用功能很少孤立地发生。如果您需要一种格式化电话号码的方法,那么您还需要一个用于验证电话号码和解析电话号码的方法。遵循YAGNI原则,你肯定不想在实际需要之前编写这些东西,但我认为继续将这些功能分成单独的类是有帮助的。从单一方法到次要子系统的那些类的增长将随着时间的推移自然发生。我发现这是保持代码长期有序,易懂和易于维护的最简单方法。
答案 5 :(得分:1)
当我创建一个应用程序时,我通常创建一个静态类,其中包含静态方法和属性,我无法弄清楚将其放在何处。
这不是一个特别好的设计,但这就是重点:它为我提供了一个地方化我认为尚未考虑的整类设计决策的地方。通常随着应用程序的增长和通过重构而得到改进,这些方法和属性实际应该存在的位置变得更加清晰。幸运的是,重构工具的状态使得这些变化通常不会特别痛苦。
我尝试过另一种方式,但另一种方法是基本上实现一个对象模型,然后才能充分了解我的应用程序来正确设计对象模型。如果我那,我花了相当多的时间和精力来提出一个平庸的解决方案,我必须在未来的某个时刻重新审视和重建。好吧,好吧,如果我知道我将重构这段代码,那么我如何跳过设计和构建不必要的复杂类的步骤呢?
例如,我构建了一个供多个客户使用的应用程序。我很早就发现,我需要找到一种方法来分离出需要针对不同客户进行不同工作的方法。我构建了一个静态实用程序方法,我可以在程序中的任何位置调用,我需要调用自定义方法,并将其粘贴在我的静态类中。
这个工作好几个月了。但有一点,它刚刚开始看起来很难看。所以我决定把它重构成自己的班级。当我查看所有调用此方法的地方的代码时,非常清楚所有自定义方法确实需要成为抽象类的成员,客户的程序集需要包含单个派生类实现所有抽象方法,然后程序只需要从其配置中获取程序集和命名空间的名称,并在启动时创建自定义功能类的实例。我找到所有必须自定义的方法非常简单,因为我需要做的就是找到我的load-a-custom-feature方法被调用的每个地方。我花了一个下午的时间来完成整个代码库并使这个设计合理化,最终结果非常灵活和强大,并且解决了正确的问题。
问题是,当我第一次实现该方法时(实际上它是三个或四个相互关联的方法),我认识到这不是正确的答案。但我不知道得出正确的答案是什么。所以我选择了最简单的错误答案,直到正确的答案变得清晰。
答案 6 :(得分:0)
我认为它不赞成的原因是因为“工具箱”可以增长,并且每次你想要调用一个函数时你将加载大量资源。
使用适用于实际类中对象的方法也更优雅 - 只是更有意义。
话虽如此,我个人并不认为这是一个问题,但仅仅因为上述原因就会避免它。
答案 7 :(得分:0)
我发表了一条评论,但我想我会详细说明一下。
我所做的是创建一个带有命名空间的公共库:[组织]。[产品]。常见的根和子命名空间助手。
这里有一些人提到的事情,比如创建一个课程并推翻一些他们不知道还能放在哪里的东西。错误。我会说,即使你需要一个辅助方法,它也与某些东西有关,所以创建一个正确命名的(IoHelper,StringHelper等)静态辅助类并将它放在Helpers命名空间中。这样,你得到一些结构,你会得到一些关注的分离。
在根命名空间中,您可以使用需要状态的实例实用程序类(它们存在!)。不用说也使用适当的类名,但不要使用Helper后缀。