代码之家  ›  专栏  ›  技术社区  ›  jpoh

使用和保持枚举的最佳实践

  •  74
  • jpoh  · 技术社区  · 7 年前

    我在这里看到了几个关于处理和持久化类枚举值的最佳方法的问题/讨论(例如 Persisting data suited for enums , How to persist an enum using NHibernate 我想问一下康塞努斯将军是什么人。

    特别地:

    • 在代码中应该如何处理这些值?
    • 它们应该如何持久化到数据库中(作为文本/作为数字)?
    • 不同解决方案的权衡是什么?

    注意:我把这个问题最初包含的解释移到了答案中。

    10 回复  |  直到 7 年前
        1
  •  18
  •   Paul Sonier    17 年前

    我同意你说的很多话。不过,关于枚举的持久性,我想附加一点:我不相信在构建时从db值生成枚举是可以接受的,但我也认为运行时检查不是一个好的解决方案。我将定义第三种方法:进行单元测试,该测试将根据数据库检查枚举的值。这可以防止“偶然”的分歧,并避免每次运行代码时根据数据库检查枚举的开销。

        2
  •  12
  •   lokori    17 年前

    第一篇文章对我来说很好。尽管如此,基于评论,似乎有一些关于Java枚举的评论可能会澄清一些事情。

    Java中的枚举类型是按定义定义的类,但许多程序员往往会忘记这一点,因为它们与一些其他语言中的“允许值列表”相关联。不止这些。

    因此,为了避免这些switch语句,可以在枚举类中放置一些代码和其他方法。几乎没有必要创建一个单独的“枚举类真实类”。

    还要考虑文档的要点-是否要在数据库中记录枚举的实际含义?在反映值(枚举类型)的源代码中还是在一些外部文档中?我个人更喜欢源代码。

    如果您想将EnUM值作为数据库中的整数,由于速度或任何原因,映射也应驻留在Java枚举中。默认情况下,您会得到字符串名称映射,我对此很满意。有一个序数与每个枚举值关联,但直接将其用作代码和数据库之间的映射并不十分明亮,因为如果有人重新排序源代码中的值,该序数将发生更改。或者在现有值之间添加其他枚举值。或者删除一些值。

    (当然,如果有人在源代码中更改了枚举的名称,那么默认的字符串映射也会出错,但这不太可能是偶然发生的。如果需要的话,您可以通过在数据库中放置一些运行时检查和检查约束来更容易地防止这种情况发生,正如这里已经建议的那样。)

        3
  •  6
  •   Quibblesome    17 年前

    在C的代码处理中,您忽略了定义delcariting的0值。 我几乎没有失败,总是将我的第一个值声明为:

    public enum SomeEnum
    {
        None = 0,
    }
    

    作为空值。因为支持类型是一个整数,而一个整数默认为0,所以在许多地方,了解枚举是否已被编程设置非常有用。

        4
  •  6
  •   jpoh    9 年前

    我试着总结一下我的理解。如果您有任何更正,请随意编辑此内容。接下来是:

    在代码中

    在代码中,枚举应该使用语言的原生枚举类型(至少在爪哇和C)中处理,或者使用类似于 "typesafe enum pattern" . 不鼓励使用普通常量(整数或类似的常量),因为您会失去类型安全性(并且很难理解哪些值是合法输入,例如方法)。

    这两者之间的选择取决于附加到枚举的附加功能的数量:

    • 如果您想将大量的功能放到枚举中(这很好,因为您可以避免一直在使用它),那么类通常更合适。
    • 另一方面,对于简单的类枚举值,语言的枚举通常更清晰。

    特别是,至少在Java中,枚举不能从另一个类继承,所以如果你有几个类似的行为,你想把它们放进一个超类中,你就不能使用Java的枚举。

    持久枚举

    要持久化枚举,应为每个枚举值分配一个唯一的ID。该ID可以是整数,也可以是短字符串。最好使用短字符串,因为它可以是助记符(使DBA等更容易理解数据库中的原始数据)。

    • 在软件中,每个枚举都应该具有映射函数,以便在枚举(用于软件内部)和ID值(用于持久化)之间进行转换。某些框架(如(n)Hibernate)对自动执行此操作的支持有限。否则,必须将它放入枚举类型/类中。
    • 数据库应该(理想情况下)为每个枚举包含一个表,列出合法值。其中一列是ID(见上文),即pk。其他列可能有意义,例如描述。所有包含该枚举值的表列都可以将此“枚举表”用作FK。这保证了不正确的枚举值永远不会被持久化,并允许数据库“独立”。

    这种方法的一个问题是合法枚举值列表存在于两个位置(代码和数据库)。这很难避免,因此通常被认为是可以接受的,但有两种选择:

    • 只保留数据库中的值列表,在生成时生成枚举类型。优雅,但意味着运行构建需要一个DB连接,这似乎有问题。
    • 在代码中定义要具有权威性的值列表。在运行时(通常在启动时)检查数据库中的值,在不匹配时投诉/中止。
        5
  •  5
  •   David Boike    17 年前

    Java或C语言应该总是使用代码中的枚举。免责声明:我的背景是C。

    如果要将该值持久化到数据库中,则应显式定义每个枚举成员的整数值,以便以后的代码更改不会意外地更改已翻译的枚举值,从而改变应用程序的行为。

    值应始终作为整数值保存到数据库中,以防止枚举名称重构。在wiki中保存每个枚举的文档,并向数据库字段添加一条注释,指向wiki页面记录类型。还将XML文档添加到包含指向wiki条目的链接的枚举类型中,以便通过intellisense使用它。

    如果使用工具生成CRUD代码,那么它应该能够定义用于列的枚举类型,以便生成的代码对象始终使用枚举成员。

    如果需要为枚举成员应用自定义逻辑,则可以使用以下选项:

    • 如果您有一个枚举myenum,请创建一个静态类myenuminfo,该类通过switch语句或任何必要的方法提供实用方法来发现有关枚举成员的其他信息。将“info”附加到类名称中枚举名称的末尾可确保它们在intellisense中彼此相邻。
    • 用属性修饰枚举成员以指定其他参数。例如,我们开发了一个EnumDropDown控件,它创建了一个用枚举值填充的ASP.NET下拉列表,EnumDisplayAttribute指定要为每个成员使用的格式良好的显示文本。

    我没有尝试过这种方法,但是对于SQL Server 2005或更高版本,理论上您可以在数据库中注册C代码,该数据库将包含枚举信息,并且能够将值转换为用于视图或其他构造的枚举,从而使DBA更容易使用转换数据的方法。

        6
  •  3
  •   cjk    17 年前

    由于所需的额外空间和较慢的搜索速度,在数据库中存储枚举的文本值不如存储整数。它的价值在于它比数字更有意义,但是数据库是用于存储的,表示层是用于使事物看起来更漂亮。

        7
  •  3
  •   Groo    17 年前

    根据我的经验,除了将选项(作为标志)传递给即时方法调用之外,使用枚举还可以导致 switch -在某个时刻。

    • 如果您打算在代码中使用枚举,那么您最终可能会得到不容易维护的代码(臭名昭著的 转换 声明)
    • 延长枚举是一种痛苦。您添加一个新的枚举项,并最终遍历所有代码以检查所有条件。
    • 使用.NET3.5,可以向枚举添加扩展方法,使它们的行为更像类。然而,以这种方式添加真正的功能并不是那么容易,因为它仍然不是一个类(您最终会使用 转换 -如果不是在其他地方,则在扩展方法中。

    因此,对于具有更多功能的枚举类实体,您应该花一些时间将其创建为一个类,并记住以下几点:

    • 要使类的行为类似于枚举,可以强制每个派生类实例化为单个实例,或者重写等于以允许不同实例的值比较。
    • 如果类类似于枚举,则意味着它不应包含可序列化状态——反序列化应该仅从其类型(如您所说的“id”类型)开始。
    • 持久性逻辑应该仅限于基类,否则扩展“枚举”将是一场噩梦。如果您使用单例模式,则需要确保对单例实例进行正确的反序列化。
        8
  •  3
  •   Yordan Georgiev    17 年前

    每次你发现自己在代码中使用“幻数”时,都会变为枚举。除了节省时间(因为当错误出现时魔法会消失…)它还可以节省你的眼睛和记忆(有意义的枚举使代码更易读和自我记录),因为猜猜看-你很可能是维护和开发你自己代码的人。

        9
  •  2
  •   MahdeTo    17 年前

    imho,关于代码部分:

    你应该 总是 对枚举使用“enum”类型,如果这样做,基本上会得到很多免费的东西:类型安全、封装和避免切换、支持一些集合,如 EnumSet 和 EnumMap 代码清晰。

    对于持久性部分,您始终可以持久化枚举的字符串表示形式,并使用enum.valueof(string)方法将其加载回去。

        10
  •  1
  •   Chalky    13 年前

    我知道这是一个旧的论坛,如果数据库中有其他东西可以直接集成到它上面呢?例如,当产生的数据库是代码的唯一目的时。然后,您将在每次集成时定义枚举。最好把它们放在数据库中。否则,我同意原来的帖子。