【问题标题】:The "Enum as immutable rich-object": is this an anti-pattern?“枚举作为不可变的富对象”:这是一种反模式吗?
【发布时间】:2011-10-24 20:41:21
【问题描述】:

我经常看到并使用带有附加属性的枚举来做一些基本的事情,例如提供显示名称或描述:

public enum Movement {
    [DisplayName("Turned Right")]
    TurnedRight,
    [DisplayName("Turned Left")]
    [Description("Execute 90 degree turn to the left")]
    TurnedLeft,
    // ...
}

并且已经有一套扩展方法来支持属性:

public static string GetDisplayName(this Movement movement) { ... }
public static Movement GetNextTurn(this Movement movement, ...)  { ... }

遵循此模式,可以将其他现有或自定义属性应用于字段以执行其他操作。就好像 enum 可以作为简单的枚举值类型,也可以作为具有多个字段的更丰富的不可变值对象:

public class Movement
{
    public int Value { get; set; } // i.e. the type backing the enum
    public string DisplayName { get; set; }
    public string Description { get; set; }
    public Movement GetNextTurn(...) { ... }
    // ...
}

通过这种方式,它可以在序列化过程中作为一个简单的字段“旅行”、快速比较等,而行为可以“内部化”(ala OOP)。

也就是说,我承认这可能被认为是一种反模式。同时,我的一部分认为这很有用,anti 可能过于严格了。

【问题讨论】:

  • 您是否在问在您不会使用枚举的地方使用此模式是否是个坏主意?这是将元数据关联到枚举值的绝佳模式。它不如类的替代品那么好。
  • 这可能被认为是对语言的“滥用”;几乎就像在没有保证的情况下一直使用dynamic
  • 不完全正确(因此评论),但我对编码文本以显示在属性或实体类附近的任何地方有疑虑。一方面,我认为它严重违反了“关注点分离”,另一方面,没有明显的本地化方法。唉,MS 似乎鼓励这种事情,所以也许我出去吃午饭了。
  • @Daniel,根据定义,扩展方法不在实体类附近。特定于 UI 的扩展方法甚至可以完全存在于 UI 层中。
  • @KirkWoll 我指的是“DisplayName”属性。

标签: c# .net extension-methods design-patterns anti-patterns


【解决方案1】:

我认为这在 C# 中是一个糟糕的模式,仅仅是因为对声明和访问属性的语言支持太弱了;它们并不是用来存储大量数据的。用非平凡的值声明属性很痛苦,获取属性的值也很痛苦。一旦你想要一些与你的枚举相关的远程有趣的东西(比如一个计算枚举上的东西的方法,或者一个包含非原始数据类型的属性),你要么需要将它重构为一个类,要么将其他东西放入一些带外的地方。

用一些包含相同信息的静态实例创建一个不可变类实际上并不难,而且在我看来,它更符合习惯。

【讨论】:

  • 这并不难,不,就枚举的声明与不可变的丰富对象而言,富枚举的实现者很痛苦,但是用法呢?虽然你对 idiom 的观点很符合我的观点。
  • 关于编写惯用代码的要点。您总是希望编写与它所在的语言/框架相得益彰的代码。
【解决方案2】:

我会说这是一种反模式。这就是为什么。让我们采用您现有的枚举(此处为简洁起见剥离属性):

public enum Movement
{
    TurnedRight,
    TurnedLeft,
    Stopped,
    Started
}

现在,假设需求扩大到更精确一点;比如说,航向和/或速度的变化,将你的“伪类”中的一个“场”变成两个:

public sealed class Movement
{
    double HeadingDelta { get; private set; }
    double VelocityDelta { get; private set; }
    // other methods here
}

因此,您有一个编码枚举,现在必须将其转换为不可变类,因为您现在正在跟踪两个正交(但仍然不可变)的属性,它们确实属于同一接口。您针对“丰富的枚举”编写的任何代码现在都必须被彻底删除和重做;然而,如果你从一堂课开始,你可能会做更少的工作。

您必须询问随着时间的推移将如何维护代码,以及富枚举是否比类更易于维护。我敢打赌,它不会更易于维护。此外,正如 mquander 所指出的,基于类的方法在 C# 中更为惯用。

还有其他需要考虑的事情。如果对象是不可变的并且是struct 而不是class,则您将获得与枚举相同的按值传递语义,并且对象的序列化大小和运行时大小的差异可以忽略不计。

【讨论】:

  • 我之前也遇到过这个问题,不能再同意了。
猜你喜欢
  • 2018-10-29
  • 2011-04-25
  • 1970-01-01
  • 2020-02-20
  • 2023-03-25
  • 2014-04-20
  • 1970-01-01
  • 2014-07-31
  • 1970-01-01
相关资源
最近更新 更多