【问题标题】:Design Perspective: Static Methods vs. Classes设计视角:静态方法与类
【发布时间】:2011-01-03 00:47:18
【问题描述】:

虽然这是一个相当普遍的问题,但我正在努力寻找最好的解决方法(如果在这种情况下需要解决的话)。

我继承了一个网站(ASP.NET,C#),其中一部分包含一个充满静态方法的类(老实说,这是一个非常大的类)。特别是一种方法是发送电子邮件。它有我能想到的所有可能的参数,而且效果很好。但是,该特定方法的内部结构管理和理解起来相当麻烦,因为所有东西都被塞进了内部——尤其是在大多数参数没有被使用的情况下。此外,由于这一方法的所有参数,处理错误有些困难。

当你想发送一封电子邮件时,实际上有一个实例化的 EMail 类会更有意义吗?这对我来说“感觉”更合适,尽管我无法完全解释原因。在这种特殊情况下,您对前进的道路有何看法?一般情况如何?

谢谢。

【问题讨论】:

    标签: c# asp.net static-methods


    【解决方案1】:

    您所描述的内容听起来像是格言的一个例子,“您可以用任何语言编写 FORTRAN。”

    一个充满静态方法的大型类通常(并非总是)表明某人只是没有“获得” OOP,陷入了过程式编程思维并试图扭曲语言来做他想做的事。

    根据经验:如果任何方法,无论是静态方法还是实例方法,都需要超过 5 个参数,这通常表明该方法试图一次做太多事情,并且很适合重构为一个或更多类。

    另外,如果静态方法并不真正相关,那么它们应该至少被拆分为实现相关功能的类。

    我真的想知道为什么你会有一个“发送电子邮件”方法,因为System.Net.Mail 命名空间几乎可以处理所有情况,并且可以通过 app.config/web.config 文件进行配置,因此您无需向其传递服务器名称或端口。这可能是一种“通知”方法 - 个别页面应该调用的东西,以便根据填充了各种值的模板发送几个“标准”消息之一,并自动添加某些页眉/页脚?如果是这样,那么对于这种类型的交互,有许多设计比您似乎继承的更容易使用。 (即MailDefinition

    更新:现在看到您的评论说这是用于异常处理,我认为最合适的解决方案是实际的异常处理程序。这方面有大量的资源。对于 ASP.NET WebForms,我实际上采用了 Jeff Atwood 多年前写的那个,将它移植到 C# 并进行了一些更改(比如忽略 404 错误)。 this previous question中有很多不错的链接。

    这些天我的偏好只是将异常处理(以及随后的异常报告的电子邮件发送)视为日志记录的一个子集。 log4net 有一个非常强大的 SmtpAppender,您可以将其配置为仅用于“致命”错误(即未处理的异常 - 在您的处理程序中,您只需进行 LogFatal 调用)。

    毫无疑问,您会从上面的 SO 链接和任何引用的链接中了解到,重要的是这里实际上有两个反模式 - “杂项”静态类和 捕获异常你不知道怎么处理。这在 .NET 中是一种不好的做法 - 在大多数情况下,您应该只捕获可以从中恢复的特定于应用程序的异常,并让所有其他异常冒泡,必要时安装全局异常处理程序。

    【讨论】:

    • 您几乎描述了它的用途 - 它被用作通知开发团队任何崩溃的一种方式。基本上,该方法在任何 catch 块中被调用(因此,它被相对频繁地调用)。过去的开发人员希望只用一行代码就能快速发送电子邮件 - 因此使用了静态方法。
    【解决方案2】:

    这里是Microsoft guidelines for when to use static types,一般来说。

    我个人想补充几点:

    • 必须使用静态类型来编写扩展方法。
    • 静态类型会使单元测试变得困难,因为它们难以/不可能模拟。
    • 静态类型强制执行不变性和引用透明的功能,这可能是一个很好的设计。因此,将它们用于设计为不可变且没有外部依赖关系的事物。例如,System.Math
    • 有些人认为 (e.g.) 单例模式是个坏主意。无论如何,将静态类型视为单例是错误的。它们比这更广泛。

    这种特殊情况有副作用(发送电子邮件)并且似乎不需要扩展方法。所以它不适合我认为静态类型的有用案例。另一方面,使用对象将允许模拟电子邮件,这将有助于单元测试。所以我认为你在这里说静态类型不合适是正确的。

    【讨论】:

      【解决方案3】:

      天哪,是的。

      这听起来像是一个移植的旧 Classic ASP 应用程序。

      它违反了single responsibility principle。如果你可以重构那个类。为那个函数使用重载

      【讨论】:

      • 我认为您的意思是“原则”。或者也许不是(非英语母语者)
      • @Martinho - 谢谢。我是最好的拼写者,我的第一语言是英语。
      【解决方案4】:

      这是Utils anti-pattern 的一个例子。

      根据它们的职责将这些方法分开总是一个好主意。创建一个电子邮件类绝对是一个好主意™。它会给你一个更好的界面来使用,它允许你在测试中模拟出电子邮件。

      【讨论】:

        【解决方案5】:

        请参阅 The Little Manual of API Design,其中描述了具有最少构造函数和大量 getter/setter 的类的好处,而不是使用具有许多参数的构造函数/方法。

        由于您提到的方法的大多数参数都没有使用,因此更好的方法是使用简单的构造函数,该构造函数假定内部变量的合理默认设置。拥有 setter 方法后,您可以设置需要非默认值的少数参数(并且仅是那些参数)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-09-11
          • 1970-01-01
          • 2011-01-17
          • 1970-01-01
          • 1970-01-01
          • 2016-09-25
          • 2011-11-10
          • 1970-01-01
          相关资源
          最近更新 更多