Java 类的大量布尔标志输入

Java 类的大量布尔标志输入,java,refactoring,Java,Refactoring,我有一个对话框,根据应用程序的状态、当前用户的安全性等显示各种内容。 我目前正在传入几个布尔标志,然后根据这些标志启用和/或隐藏UI组件。例如: new MyDialog(showOptionsTable, allowFooInput, allowBarInput, isSuperUser) 起初,这是一对旗帜,这很好。但是现在随着需求的变化,它已经演变成五个布尔标志的输入 处理这种行为的最佳做法是什么?这是我应该根据对话框的外观来分类的吗? 一旦你获得了超过两个或三个标志,我会考虑创建一个

我有一个对话框,根据应用程序的状态、当前用户的安全性等显示各种内容。 我目前正在传入几个布尔标志,然后根据这些标志启用和/或隐藏UI组件。例如:

new MyDialog(showOptionsTable, allowFooInput, allowBarInput, isSuperUser) 
起初,这是一对旗帜,这很好。但是现在随着需求的变化,它已经演变成五个布尔标志的输入


处理这种行为的最佳做法是什么?这是我应该根据对话框的外观来分类的吗?

一旦你获得了超过两个或三个标志,我会考虑创建一个类来存储这些设置以保持你的设计干净。

< P>使用动态添加行为到你的对话框

< P>设置My对话框(false,false,…)是预期的默认行为。(即:最常见的情况应该是all false。您可能需要反转标志的语义。)

现在,定义常量:

OPTION1 = 1
OPTION2 = 2
OPTION3 = 4
OPTION4 = 8
...
更改该方法以获取int options参数

public void MyDialog(int options) ...
现在叫它:

MyDialog(OPTION1 | OPTION3)  // enable Opt1, opt2)
方法内部:

if (options & OPTION1) // use Option 1 config.

等等。

如果GUI取决于应用程序的状态(一种状态导致另一种状态),您可以查看模式。其中,每个新状态将由不同的对象处理,您可以对标志是否应为go进行编码

并使用此状态显示对话框

new MyDialog(showOptionsTable, new InitialState() );

当应用程序的状态更改时,您将更改状态对象

public void actionPerfomed( ActionEvent e ) { 
    this.state = state.next();
    repaint();
 }
要绘制对话框的各个部分,请查询状态:

  if( state.getFlags()[SECURITY] ) { 
      /// show security stuff
  } if ( state.getFlags()[VIEW_ONLY] ) { 
      // enable/disable stuff 
  } ....
您可以更进一步,让状态定义呈现的内容

abstract class State { 
      public abstract JComponent getComponent();
      public abstract State next();
 }   
因此,每个州都显示了不同的部分:

 Dialog.this.setContentPane( state.getComponent() );

创建一个类以保存配置选项:

public class LayoutConfig
{
    public boolean showOptionsTable = true;
    public boolean allowFooInput = true;
    public boolean allowBarInput = true;
    public boolean isSuperUser = true;
}

...

LayoutConfig config = new LayoutConfig();
config.showOptionsTable = false;

new MyDialog(config);

这种方法可以轻松添加新选项,而无需更改界面。它还允许您添加非布尔选项,如日期、数字、颜色、枚举…

要在Ben Noland answer的基础上构建,您可以将一些选项定义为枚举,然后使用varargs构造函数:

class MyDialog {
   enum DialogOptions {
      SHOW_OPTIONS_TABLE, ALLOW_FOO_INPUT, ALLOW_BAR_INPUT, IS_SUPER_USER
   }
   public MyDialog(DialogOptions ...options) { ... }
}

...
new MyDialog(DialogOptions.ALLOW_FOO_INPUT, DialogOptions.IS_SUPER_USER);
与许多事情一样,“这取决于”

  • Ben Noland建议使用一个类来保存配置选项。这是可行的,但支持不变性,并且可以选择使用构建器模式。因为布尔值是内置类型,所以编写一个小型构建器将真正帮助人们理解代码。如果您将其与MyDialog(true,true,…)进行比较,您知道我的意思:

    Options.allowThis().allowThat().build()选项

  • Chris建议使用位字段,但正如一些评论者所指出的,位字段是邪恶的,因为Josh Bloch在《有效Java》中概述了许多原因。基本上,它们很难调试并且容易出错(您可以传入任何int,它仍然可以编译)。因此,如果您走这条路线,请使用实枚举和枚举集

  • 如果您可以合理地子类化(或组合),这意味着您通常只使用所有布尔值的几个组合,那么就这样做

  • 我发现,如果我使用
    enum
    s作为布尔选择,这类内容的可读性会更高

    public enum ShowOptionsTable { YES, NO }
    public enum AllowFooInput { YES, NO }
    public enum AllowBarInput { YES, NO }
    public enum IsSuperUser { YES, NO }
    
    new MyDialog(ShowOptionsTable.YES, AllowFooInput.NO, AllowBarInput.YES,
                 IsSuperUser.NO);
    

    有了这样的枚举,使用带有大量布尔参数的代码变得容易理解。此外,由于您使用对象而不是布尔值作为参数,因此如果需要,您可以使用其他模式在以后轻松重构,如使用装饰器、外观或其他模式。

    如果参数都是布尔值,我更喜欢标记的枚举而不是设置类。如果你不能保证将来的安全,那就比后悔好。下面是标志的另一个实现:

    [Flags]
    public enum LayoutParams
    {  
        OptionsTable = 1,  
        FooInput = 2,  
        BarInput = 4,  
        SuperUser = 8,
    }
    
    public MyDialog(LayoutParams layoutParams)
    {
        if (layoutParams & LayoutParams.OptionsTable)
        { /* ... Do Stuff ... */ }
    }
    
    public static MyDialog CreateBasic()
    {
        return new MyDialog(LayoutParams.OptionsTable | LayoutParams.BarInput);
    }
    

    根据您的显示将有多大不同,您可以考虑对显示类进行分类(即<代码> MyDialogSuperUser <代码>或SoMeSuCh)。你需要考虑到对话类输入的正交性以及如何表达正交性。

    < P>我有一个最喜欢的方式来处理这个问题,但是它对于所有用例都是无效的。如果布尔值不是完全独立的(比如说存在一些无效的布尔值组合,或者布尔值组合是通过可识别的场景实现的。)我为状态创建一个枚举,然后定义一个保存标志的构造函数:

    public enum status {
        PENDING(false,false),
        DRAFT(true,false),
        POSTED(false,true),
        ;
        public boolean isSent;
        public boolean isReceived;
        status(boolean isSent, boolean isReceived) {
            this.isSent = isSent;
            this.isReceived = isReceived;
        }
    }
    
    这样一段代码的优点是,可以相对简洁地构造枚举常量,但仍然允许代码只关注状态的一个特定方面。例如:

    //I want to know specifically what the state is
    if (article.state == status.PENDING)
    // Do something
    
    //I really only care about whether or not it's been sent
    if (article.state.isSent)
    // Do something
    
    //I want to do something specific for all possible states
    switch(article.state)
    // A string of case statements
    
    另一个优点是,如果您定义好了枚举,则永远不会达到非法状态:

    if (article.state.isReceived && !article.state.isSent) {
    // This block can never execute ever.
    }
    

    当然,并不是所有的时候布尔人之间都有逻辑关系,但我确实建议把它们映射出来。如果布尔数的子集具有逻辑关系,那么将其分解为枚举可能是值得的。

    也就是说,重构和子类化可能是一个更好的选择。我更喜欢枚举而不是原始整数。是的,但保持简单愚蠢——这只是修饰。修饰,这可能是C语言中的标准方式,但在Java中肯定是一种代码味道。至少我会在代码审查中批评它。我不想让我的API设计人员要求我们的API用户只需选择几个选项就可以进行位操作。您可以发布装饰器模式的伪示例吗?包含使用Window和WindowDecoratorTanks作为链接的经典示例。我不知道装饰图案的概念,但我不知道如何在这里应用它-/您可以为每个布尔值编写一个Decorator:即OptionTableDecorator、SuperUserDecorator等。这种设计允许您动态地向原始对话框添加新行为,您的布尔值被转换为一个Decorator链,维护起来更简单。希望有帮助:)你是说一个具有属性的JavaBean吗?当添加更多的复选框时,这将导致创建的状态数量呈指数级增长。如果这部分代码不太可能在t中增加,状态模式可能会更好
    if (article.state.isReceived && !article.state.isSent) {
    // This block can never execute ever.
    }