【问题标题】:Using enum vs Boolean?使用枚举与布尔值?
【发布时间】:2011-05-19 06:51:52
【问题描述】:

这可能最初看起来很笼统,但实际上,我实际上正在做出我需要使用的决定。

我目前正在处理的工作涉及就业申请,这些申请需要在某个时间点标记为 ActiveInactive。提交申请时,默认为Active。由于某些原因,它稍后可能会设置为 Inactive。它只能是其中之一,并且永远不会 null(如果这会改变任何东西)。

我将它与 Java + Hibernate + PostgresSQL 一起使用,以防这也有什么不同。我的第一直觉是使用 Boolean 作为我的解决方案,以便它真正充当标志,但我有同事建议使用 enumsints 更多的是一种状态,而不是一个标志。

我已经使用上述所有解决方案解决了诸如此类的问题,而且它们似乎对彼此都有些透明。

有没有一种更适合这种情况的方法?

【问题讨论】:

标签: java hibernate postgresql


【解决方案1】:

即使忽略将来添加更多状态类型的可能性(这肯定是enum 的一个很好的论据),我认为enum 绝对是正确的方法。您不是在为布尔条件建模,而是在为应用程序的状态建模。想一想:应用程序的状态不是truefalse,它是活动的还是非活动的!状态 enum 将以最自然的方式表示这一点。

您还可以从使用枚举中获得很多内置优势,例如每个状态的文本描述直接与其相关联,因此您不必做类似的事情

String text = application.isActive() ? "Active" : "Inactive";

你可以这样做

String text = application.getStatus().toString();

此外,您可以使用每个枚举以不同方式实现的抽象方法将特定行为直接绑定到每个状态,将特定数据与每个状态相关联,等等。

您还可以轻松地允许基于状态的布尔 isActive 检查...如果您只存储 boolean,则无法轻松地反过来做。

public boolean isActive() {
  return status == Status.ACTIVE;
}

null 不应该是一个有效状态这一事实是无关紧要的......只要确保任何存储状态的类(比如,你的 EmploymentApplication 类或其他)如果有人尝试,就会抛出 NullPointerException在其上设置null 状态。

【讨论】:

  • ColinD,null 状态肯定与 Hibernate 和 PostgreSQL 相关。应用程序确实不需要抛出NullPointerException,因为如果数据库表中的状态列不能为空,那么 JDBC 驱动程序将抛出异​​常。如果属性的 Hibernate 映射不允许空值并且应用程序尝试为该属性保留空值,那么 Hibernate 将抛出 org.hibernate.PropertyValueException: not-null property references a null or transient value
  • @Derek:当然......我没有提到数据库方面,但我要说的是,如果null 不是有效状态,您只需要确保如果null 状态以某种方式进入系统,应用程序会很快失败。它越早失败,就越容易确定 null 来自哪里并更正它......然后假设状态不是 null 在代码中使用该状态的任何地方。
  • +1,我非常接近让自己成为另一个帐户,只是为了再次投票。
【解决方案2】:

这完全取决于您的要求/规格。如果只想将状态记录为活动或非活动,最好的方法是使用boolean

但如果在未来,你会有这样的状态,

  • 活动中
  • 无效
  • 已暂停
  • 已屏蔽

枚举非常适合您。就您而言,目前,布尔值就足够了。不要过早尝试使事情变得过于复杂,否则您将失去对系统设计和开发的关注。

【讨论】:

  • 我强烈反对使用枚举是“过于复杂”的事情。枚举是“活动”和“非活动”比“真”和“假”更自然的表示,并且具有许多优势,允许与这些状态相关的代码(包括为每个状态获取名称)集中他们的定义,而不是分散在代码的其他地方。
  • @ColinD,我的建议是,如果一个状态基本上是活动/非活动,一个简单的private boolean active 就足够了。在 OP 知道状态的确切规范之前,没有必要创建您不知道的值的枚举。此外,在 PostGres 中表示布尔字段比在系统中添加附加值时总是更改表字段添加新值更简单。目前,OP 应该从提供的内容开始。
  • 使用多个布尔字段(例如activesuspendedblocked)的好处是以后添加新状态不会影响现有代码和查询,以及那些条件表达式通常会更简单。也就是说,一个条件可能是active==false,而不关心它为什么恰好处于非活动状态(暂停或阻止或其他一些未来状态)......当然,缺点是需要维护更多字段和更多数据 - 以及无效的风险/冲突状态,例如active=falsesuspended=true 同时,您的设计/代码需要防止。
【解决方案3】:

绝对不要使用 int。使用枚举是面向未来的;您必须自己决定什么更具可读性,以及YAGNI 是否适用。请注意booleanBoolean 不同; Boolean 是一个类名,因此,Boolean 类型的变量可以为 null;而boolean 是一个原语。

【讨论】:

    【解决方案4】:

    布尔值在商业和工业中很少见

    根据我为企业构建自定义应用程序的经验,“女性/男性”或“开/关”等明显的二元性几乎总是会演变或演变为多个值。

    • “女/男”变为“未知/女/男”
    • “开/关”变为“开/暖机/冷却/关”
    • “未完成/已完成”变为“未完成/处理中/已完成”

    业务规则通常在早期并不完全了解,或者随着时间的推移而变化。

    像我的许多同事一样,我学会了永远不要将此类值定义为布尔值的艰难方法。以后从布尔值更改为多个值是非常昂贵和冒险的。

    Java 枚举

    枚举比布尔值有其他好处。

    Java 枚举灵活而强大

    Java 中的Enum 工具比大多数其他语言中的枚举更加灵活、强大和有用。见Oracle Tutorial

    在您的枚举定义中,您可以将值的表示形式提供给用户并存储在数据库或文件中。这为程序员提供了一个方便的一站式商店来阅读有关此枚举的所有信息以及它在您的应用中的使用方式。

    这是一个完整的例子。这一类显示了我们在用户界面中使用的值以及我们保留的值。

    package com.basilbourque.example;
    
    public enum Status {
        ACTIVE( "Active" , "active" ),
        INACTIVE( "Inactive" , "inactive" ),
        UNKNOWN( "Unknown" , "unknown" );
    
        private String displayName, codeName;
    
        Status ( String displayName , String codeName ) {
            this.displayName = displayName;
            this.codeName = codeName;
        }
    
        public String getDisplayName () { return this.displayName; }  // Or even add a `Locale` argument to support localization.
    
        public String getCodeName () { return this.codeName; }
    
        // To find a `Status` enum object matching input retrieved from a database.
        static public Status ofCodeName ( String codeName ) {
            // Loop each of the enum objects to find a match.
            for ( Status s : Status.values() ) {
                if ( s.getCodeName().equals( codeName ) ) { return s; }
            }
            throw new IllegalArgumentException( "No such Status code name as: " + codeName );
        }
    
        @Override
        public String toString() { return "app-status-" + this.getCodeName() ; }
    
    }
    

    将持久化的值转换回枚举对象

    请注意可以返回与从数据库中检索到的持久值匹配的Status 对象的静态方法:

    Status s = Status.ofCodeName( myResultSet.getString( "code" ) ) ;
    

    灵活的集合/地图

    在 Java 中,枚举有自己的 SetMap 实现,它们在使用的内存非常少和执行速度非常快方面都经过了高度优化。

    Set< Status > auditApprovedStatuses = EnumSet.of( Status.ACTIVE , Status.INACTIVE ) ;  
    Set< Status > auditAlertStatuses = EnumSet.of( Status.UNKNOWN ) ;
    
    …
    
    if( auditAlertStatuses.contains( application.getStatus() ) ) {
        this.alertAuditor( application ) ;
    }
    

    请注意,如果您的应用程序状态定义发生变化,更新这些集合定义是多么容易。

    信息丰富的toString

    虽然布尔值显示为 truefalse 作为字符串,但您可以在枚举中覆盖 toString 以生成更多信息值,例如 app-status-inactive

    这些值在记录、跟踪或调试时可能非常有用。

    【讨论】:

      【解决方案5】:

      不能两者兼得吗?

      enum Status {
          ACTIVE(true), INACTIVE(false);
          private final boolean value;
          Status(boolean value) {
              this.value = value;
          }
          boolean getValue() {
              return this.value;
          }
      }
      

      这不是让您两全其美吗?它允许您使用名称为 ACTIVE 和 INACTIVE 的布尔值,而不是 true 和 false?

      【讨论】:

        【解决方案6】:

        如果 true/false 是唯一的可能性,则 boolean 比 enum 更有意义(开销更少)。建议:使用布尔类而不是布尔原语,这样您就可以检测“未知/未定义”状态以及真/假。

        【讨论】:

        • 在这种情况下,由于状态不能为空,booleanBoolean 更可取,因为 Boolean 接受映射到表中 NULL 列值的空值,但是boolean 永远不能为空。
        【解决方案7】:

        如果您可能需要除 Active 和 Inactive 之外的更多状态,那么您会想要使用 enum 或 int 状态标志吗?这使您的代码对未来状态更加灵活。

        【讨论】:

          【解决方案8】:

          在您的情况下,具有布尔值就足够了。由于要求是“IsActive”,并且立即的答案可以是真或假。有一个枚举是可以的,但 IMO,一个布尔值是合适的

          【讨论】:

            【解决方案9】:

            我认为最好使用 Enum 而不是 Boolean。在 Effective java 中,它更喜欢二元素枚举而不是布尔值。它还提出了它的以下优点。 代码更容易阅读 • 代码更易于阅读 • 代码更容易编写(尤其是使用 IDE) • 减少查阅文档的需要 • 更小的错误概率 • 更适合 API 演进

            请参阅以下链接以获取更多详细信息。 https://www.cs.umd.edu/class/fall2009/cmsc132H/slides/still-effective.pdf

            【讨论】:

              【解决方案10】:

              我更喜欢枚举器,因为我与远程团队合作理解他们的代码,然后在深夜编写包含样板代码的测试用例我相信使用 enum 比@987654322 更好 @ 不仅有助于良好的设计,还可以减少思考的负担并提高可读性。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-05-10
                • 1970-01-01
                • 2023-03-04
                • 1970-01-01
                • 2017-09-07
                • 1970-01-01
                相关资源
                最近更新 更多