两个成员的枚举类型与布尔值

11

这个问题在我脑海中已经存在一段时间了,如果它看起来很主观,对不起。在公共属性和数据对象的构造函数中使用bool存在一些缺点。考虑以下代码作为例子。

使用bool:

public class Room
{
    public string Name { get; set; }
    public bool Bookable { get; set; }

    public Room(string name, bool bookable);
}

以及使用这个类

Room r = new Room ("101", true);

这是一个合适的、功能良好的实现方法,但还有另一种实现方式:

使用枚举:

public enum BookingStatus
{
    Bookable,
    NotBookable
}

public class Room
{
    public string Name { get; set; }
    public BookingStatus Bookable { get; set; }

    public Room(string name, BookingStatus bookable);
}

以及使用这个类的方式

Room r = new Room ("101", BookingStatus.Bookable);

在我看来,这两种方法在功能上看起来是等效的,但是每种方法都有一些优势和缺点:

  • 当设置属性时,枚举方法更冗长(你可以从代码中推断出枚举的使用)
  • 枚举可以扩展以支持进一步的状态(特别适用于API)
  • 枚举需要大量的输入(尽管可以大大减少这个数量)
  • 枚举不能在条件语句中使用(例如if (r.bookable)),尽管我认识到这很容易解决。

我是否漏掉了什么,完全不对吗?我不确定为什么这让我如此烦恼,也许是因为我太过于强迫症了!


观点:这似乎不是最好的例子——如果它是一个真正的是/否问题,那么布尔值是合适的。虽然你应该根据惯例正确地命名你的属性(即“IsBookable”)——一个更好的例子是一个非是或否的问题,比如性别或计量单位等——即使你只认识两种状态(英寸 vs 厘米,或男性 vs 女性),你也应该使用枚举,因为这些绝对不是“是或否”的问题(即使你可以把它们变成是/否问题,它们并不是,每个问题都可以扩展支持更多的状态)。 - BrainSlugs83
4个回答

11

出于代码的可读性和理解性,我会选择枚举而不是布尔值。

比较 BookingStatus.Bookabletrue,当然你阅读 BookingStatus.Bookable 会更有理解力。

正如 fforw 提到的那样,如果将来需要添加更多选项,枚举会更容易修改。


类型安全怎么样?枚举可以防止任何旧的布尔值被放入枚举参数中。 - weberc2
1
如果新的BookingStatus类型被发明,使用枚举的话,更容易维护。因为变化会更小。 - Harald Coppoolse

5

如果是关于属性,我可能会同意。但是在《代码整洁之道》中指出(正确地),当用作参数时,bool 会隐藏意图。例如,格式化方法可能像这样:

public void Format(
   bool bold,
   bool italic,
   bool underline
) {
   ...
}

当调用时,它看起来像这样:

Format(true, false, true);

相反,将其改为以下方式会更易读:

public enum Bold { Yes, No }
public enum Italic { Yes, No }
public enum Underline { Yes, No }

public void Format(
   Bold bold,
   Italic italic,
   Underline underline
) {
   ...
}

这将使调用看起来像:

Format(Bold.Yes, Italic.No, Underline.Yes);

请记住,这只是bool参数隐藏意图的一个例子;可能有更好的方法来实现上述代码。

在您的特定情况下,请注意两个构造函数调用之间的区别:

// enum; intent is clear
Room r = new Room ("101", BookingStatus.Bookable);

// bool; true what?
Room r = new Room("101", true);

使用参数名称也可以改进此功能:

Room r = new Room("101", bookable: true);

3
另外,当使用枚举类型时,您不能将包含应为加粗的值的变量放入“italic”参数槽中。 :) 这是类型安全性。 - weberc2
// 字符串;“101”是什么? 添加更多的字符串,你就会像bool一样处于同样的位置。那么你该怎么办呢?你需要查看方法定义。 - user247702
@Stijn 同意。不读方法签名不是不理解方法签名的借口。 - Dan Lugg

5
如果将来可能会出现不止两个选项,那么将枚举添加第三个选项比将所有 bool 更改为枚举要少得多的工作量。

这就是他在回答中已经说过的(项目符号列表中的第二点)。 - Stefan Steinegger
除了“未来计划”方面。 - fforw

2
在他的书《重构》中,Martin Fowler解释了为什么他认为枚举是一种代码异味,我完全同意。在您的示例中,更好的方法是创建一个抽象的Room类:
public abstract class Room
{
     public string Name { get; set; }

     public abstract bool Bookable { get; }
}

然后,您可以创建派生的BookableRoom和NonBookableRoom类。
public class BookableRoom : Room
{
    public override bool Bookable
    {
        get { return true; }
    }
}

public class NonBookableRoom : Room
{
    public override bool Bookable
    {
        get { return false; }
    }
}

强项,以及我在其他地方使用的方法。我猜这对于像实体框架这样的东西不太适用?但是它肯定非常适合映射到像SharePoint列表这样的东西。 - Richard Slater
15
你为什么认为这是更好的方法?在我看来,这只是增加了复杂性。我认为在这里应该采用KISS原则。 - nightcoder
本书提供了全面的描述。 - Mark Seemann
6
我同意@nightcoder的观点,对选择这个答案感到困惑。OP的关注点是枚举不足以复杂和冗长地翻转一个位吗?那么我们应该将每个对象类型的每个运行时状态都编写进我们的源代码中吗?究竟马丁·福勒为何要支持这种做法? - harpo
2
那么当您有多个状态属性时,该怎么办呢?添加更多的类吗?添加提供每种可能状态组合的类吗? - user247702
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接