【问题标题】:What is the best approach for implementing enable/disable methods in Java?在 Java 中实现启用/禁用方法的最佳方法是什么?
【发布时间】:2011-08-07 04:42:09
【问题描述】:

在 Java 中,给定一对公共方法 enableFoo 和 disableFoo,它们分别将名为 isEnabledFoo 的布尔字段设置为 true 或 false,该方法是否应该在设置之前检查 Foo 是否已启用?如果是,是否应该抛出异常?如果是,应该勾选还是不勾选?如果未选中,它应该抛出什么?非法状态异常?

一个转折:虽然当前的实现只是设置了一个布尔字段,但我故意不将它实现为一个带有布尔参数的 setFoo“setter”方法,因为以后可能会更改实现以包含副作用(甚至可能不完全设置一个字段)。将其保留为 enableFoo/disableFoo 似乎是保证封装的最佳方式。

我应该这样做吗:

public void enableFoo() throws myCheckedException
{
  if (! this.isEnabledFoo)
  {
    this.isEnabledFoo = true;
  }
  // Is this overkill?
  else
  {
    // Foo is already enabled...Should a checked exception be thrown?
    throw new myCheckedException ("Foo is already enabled.");

    // What about an unchecked exception?
    // throw new IllegalStateException ("Foo is already enabled.");
  }
}

或者只是这样:

public void enableFoo()
{
  // I guess we don't care if foo is already enabled...
  this.isEnabledFoo = true;
}

甚至:

public void enableFoo()
{
  // Is this just code bloat?
  if (! this.isEnabledFoo)
  {
    this.isEnabledFoo = true;
  }
}

我认为虽然最后两个版本更简单,但它们会隐藏一个可能的错误,即开发人员在调用 enableFoo 时认为它已被禁用,而实际上它已经被启用(但这真的很重要吗?)。这里最好的设计是什么?

【问题讨论】:

    标签: java exception methods boolean state


    【解决方案1】:

    不。你为什么要关心? enableFoo 和 disableFoo 非常明确 - 一个启用,另一个禁用。无需检查当前状态。

    如果你真的想要,你可以返回一个布尔值来表示之前的状态。你也可以有一个public boolean isEnabled() 方法。

    【讨论】:

    • 是的,但是有人会在打电话之前花时间检查之前的状态吗?我个人认为他们不会,特别是如果两次调用 enableFoo 不会引发任何异常。
    • 这就是重点——如果调用它两次不是问题(因此不需要知道),那么也不需要抛出异常。您的方法名称传达了功能。举个例子,看看AbstractAction - setter (setEnabled(boolean)) 不关心当前状态是什么,如果你想检查,有一个isEnabled()。
    【解决方案2】:

    没有直接的答案。这取决于要求。具体来说你的操作是否是幂等的。

    http://en.wikipedia.org/wiki/Idempotence

    【讨论】:

    • 感谢您的启发性文章。一般情况下,setter 是否应该是幂等的?
    • 我想考虑两种类型的属性(成员变量):为对象提供标识的属性和提供行为的属性。身份的设置器通常可以是幂等的,但行为属性的设置器不必是幂等的。作为一个经验法则,如果文档没有说不要假设任何方法(包括 setter)是幂等的。
    • 您能否提供一个行为与身份属性的示例?我不熟悉这个术语。
    • 澄清一下,我认为行为 == 方法和身份 == 属性。
    • 术语是我编造的 ;-)。
    【解决方案3】:

    如果您的程序是多线程的,建议锁定启用和禁用函数,如果锁定不是原子的,检查可能会有所帮助。否则你不必担心检查状态。

    【讨论】:

    【解决方案4】:

    我真的不认为你的问题有答案。所有这些似乎都是有效的,这取决于您正在执行的操作以及操作后对象的一致状态。

    例如:我有一个启用语法突出显示的方法,如果您选择启用它并且它已经启用,您会关心什么。在这种情况下,您只需设置布尔标志,而不管之前的状态如何(选项 2)。

    另一方面,如果我不只是更改布尔字段,而是执行一些复杂的逻辑,这些逻辑可能重新运行起来可能很昂贵,例如检查文档的 DOM 并为所有正确的地方着色,我不会仅仅因为有人“重新检查”了启用标志,就想再做一次。

    类似这样的:

    public void enableHighlighting() {
      if (!isHighlighting) {
        isHighlighting = true;
        colorView();  // very expensive method
      }
    }
    

    现在让我们假设您有一个情况,其中双重启用反映了机器的不一致状态(类似于您将使用 assert 的情况。在这种情况下,您希望抛出一个异常来表示用户做了一些非法的事情.

    例如,假设语法高亮只影响某些类型的文件,例如 XML 或 Java 代码,并且用户尝试将其应用于 PHP 代码。现在突出显示已经启用,但沮丧的用户尝试启用它,因为他没有看到任何变化。在这种情况下,您可能想向用户打印一条消息,说明突出显示已启用。在这种情况下,是否发生更改的返回值可能更有意义,但您也可以使用异常。

    返回值:

    public boolean enableHighlighting() {
      prevState = isHighlighting;
      isHighlighting = true;
      return prevState != isHighlighting; //return true if value changed
    }
    

    【讨论】:

    • 好的,所以据我了解您的回答,setter 通常应该是无能的,除非有很好的理由不这样做(昂贵等)。我认为如果没有文档表明其他情况,开发人员可能会认为它是不幂等的。
    • @Aaron 很好地使用了“幂等”这个词。为了将来参考,您可能需要注意它的第二个位置没有“n”的拼写。
    【解决方案5】:

    您可以使用Facet 来启用/禁用功能。

    免责声明:我是平台的作者之一

    您可以将构面视为应用程序的所有方法。您可以在运行时启用/禁用方法。

    如何整合:

    • 从maven central获取包裹
    • 将其添加到 gradle 配置中:implementation 'run.facet.agent.java:facet-agent:0.0.8'
    • 创建facet.yml:
    workspaceId: WORKSPACE~ID
    name: My-Application
    environment: dev
    apiKey: API_KEY
    

    注意:您可以从Facet dashboard获取这些密钥

    • 集成完成后,您可以从系统实时启用/禁用方法。请注意,无需重新部署您的应用程序或重新启动它。

    为了让您了解这与其他人提到的功能标志有何不同,您可以认为构面会自动应用于您的应用程序,而不是在每次开发功能时都以编程方式声明。

    注意:这还没有准备好生产。很高兴与您一起解决问题!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-05
      相关资源
      最近更新 更多