标志参数

2011年6月23日

标志参数是一种函数参数,它根据其值指示函数执行不同的操作。假设我们要预订一场音乐会。 有两种方式可以做到这一点:普通和高级。 要在此处使用标志参数,我们最终会得到如下所示的方法声明

    //pseudo-code
    class Concert...
      public Booking book (Customer aCustomer, boolean isPremium) {...}
  

我对标志参数的一般反应是避免使用它们。 我更喜欢定义单独的方法,而不是使用标志参数。

    class Concert...
      public Booking regularBook(Customer aCustomer) {...}
      public Booking premiumBook(Customer aCustomer) {...}
  

我这样做的理由是,当我进行调用时,单独的方法更清楚地传达了我的意图。 无需记住看到 book(martin, false) 时标志变量的含义,我可以轻松阅读 regularBook(martin)

混乱的实现

我普遍不喜欢标志参数确实有一些微妙之处和后果,首先是如何处理混乱的实现。

在最简单的情况下,对标志的反应实际上是调用不同的方法。

    public Booking book (Customer aCustomer, boolean isPremium) {
      if(isPremium) 
       // logic for premium book
      else
       // logic for regular booking
    }
  

但有时逻辑更加混乱

    public Booking book (Customer aCustomer, boolean isPremium) {
      lorem().ipsum();
      dolor();
      if(isPremium)
        sitAmet();
      consectetur();
      if(isPremium)
        adipiscing().elit();
      else {
        aenean();
        vitaeTortor().mauris();
      }
      eu.adipiscing();
  

在这种情况下,尝试将常规和高级预订方法提取到单独的方法中可能会很麻烦,而这两种方法之间没有明显的重复。 在这种情况下,一种选择是保留带有标志参数的方法,但将其隐藏。

    class Order...
      public Booking regularBook(Customer aCustomer) {
        return hiddenBookImpl(aCustomer, false);
      }
      public Booking premiumBook(Customer aCustomer) {
        return hiddenBookImpl(aCustomer, true);
      }
      private Booking hiddenBookImpl(Customer aCustomer,  boolean isPremium) {...}
  

这里的重点是只有常规和高级预订方法应该调用 hiddenBookImpl。 我喜欢用一个难看的名字来表达这一点 - 这还有一个好处,那就是,如果您必须这样做,您可以轻松添加一个正则表达式探针以确保没有其他人调用它。

派生标志

如果是否使用高级预订流程的决定取决于客户的状态,该怎么办。 假设精英客户获得高级预订,而普通客户获得常规待遇。 在这种情况下,我们当然不应该有布尔标志 - 但是客户对象本身是否充当标志?

我会将其视为捕获调用者的意图。 如果预订方式仅取决于客户的状态,则调用者无需关心高级预订和常规预订之间的区别 - 因此预订例程根据客户状态得出其真实方法是完全合理的。 只有当调用者需要指定她想要哪种方法时,您才需要不同的方法。

布尔设置方法

与此相关的是如何命名布尔设置方法的问题。 在这里,我同意 Kent 的建议 - 我宁愿看到

    void setOn();
    void setOff();
  

而不是看到

    void setSwitch(boolean on);
  

但再次同意 Kent 的观点,这确实取决于方法的使用方式。 如果您从布尔源(例如 UI 控件或数据源)提取数据,我宁愿使用 setSwitch(aValue) 而不是

    if (aValue)
      setOn();
    else
      setOff();
  

这是一个 API 应该编写为方便调用者的示例,因此如果我们知道调用者来自哪里,我们应该在设计 API 时考虑到这些信息。 这也表明,如果我们以两种方式接收调用者,我们有时可能会同时提供两种样式。

同样的逻辑也适用于 book。 如果屏幕上有一个复选框,而我们只是将其值传递给 book,那么标志参数就有一定的道理。 在这个例子中,我不会说这是一个简单的选择 - 大多数情况下,我认为 book 的标志参数比简单的布尔设置器更难理解,因此值得使用显式方法。