很多布尔标志输入到类

时间:2009-05-08 18:20:05

标签: java refactoring

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

new MyDialog(showOptionsTable, allowFooInput, allowBarInput, isSuperUser) 

最初,这开始是一些标志,这很好。但是现在随着需求的变化,它已经演变为五个布尔标志的输入。

处理此类行为的最佳做法是什么?这是否应根据对话框的外观进行子类化?

11 个答案:

答案 0 :(得分:10)

与许多事情一样,“这取决于”。

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

    Options.allowThis()。allowThat()。建立()

  2. Chris提出了比特字段,但正如一些评论者指出的那样,由于Josh Bloch的Effective Java中列出的许多原因,比特字段是邪恶的。基本上他们很难调试和容易出错(你可以传入任何int,它仍然会编译)。因此,如果你走这条路线,请使用真实的枚举和EnumSet。

  3. 如果你可以合理地继承(或撰写),意味着你通常只使用所有布尔值的几个组合,那就这样做。

答案 1 :(得分:8)

一旦你获得两个或三个以上的旗帜,我会考虑创建一个类来存储这些设置,以保持你的设计清洁。

答案 2 :(得分:3)

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

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);

这种方法可以在不更改界面的情况下轻松添加新选项。它还允许您添加非布尔选项,如日期,数字,颜色,枚举......

答案 3 :(得分:2)

使用decorator pattern为您的对话框动态添加行为

答案 4 :(得分:2)

要建立Ben Noland的答案,你可以将一些选项定义为枚举,然后有一个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);

答案 5 :(得分:1)

我发现如果我使用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);

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

答案 6 :(得分:-1)

如果参数都是boolean,我更喜欢将标记的枚举设置为设置类。如果你不能保证在将来虽然它会比抱歉更安全。这是标志的另一种实现:

[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);
}

答案 7 :(得分:-1)

根据您的显示器的不同程度,您可以考虑为您的显示类(即MyDialogSuperUser或其他类似物)进行子类化。您需要考虑对话类的输入与正交的正交性以及如何表达正交性。

答案 8 :(得分:-1)

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

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.
}

当然,并不是所有布尔人之间都存在逻辑关系,但我建议将它们映射出去。如果布尔的一个子集具有逻辑关系,则可能值得将它们分解为枚举。

答案 9 :(得分:-2)

设置它以便MyDialog(false,false,.....)是预期的默认行为。 (即:最常见的情况应该是假的。你可能需要反转标志的语义。)

现在,定义常量:

OPTION1 = 1
OPTION2 = 2
OPTION3 = 4
OPTION4 = 8
...

更改方法以获取int选项参数

public void MyDialog(int options) ...

现在打电话给:

MyDialog(OPTION1 | OPTION3)  // enable Opt1, opt2)

在方法内:

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

答案 10 :(得分:-2)

如果GUI 取决于应用的状态(其中一个状态指向另一个状态)您可以查看State模式。每个新状态将由不同的对象处理,您可以编写标志是否应该进行编码。

abstract class State { 
      public abstract boolean [] getFlags();
      public abstract State next();
 }
 class InitialState extends State  { 
      public boolean [] getFlags() {
          return new boolean [] { true, true, false, false, false };
      }
      public State next() { return new MediumState(); }
 }     
 class MediumState extends State { 
     public boolean [] getFlags() { 
         return new boolean[] { false, false, true, true, false };
     }
     public State next() { return new FinalState(); }
 }
 class Final extends State { 
     public boolean [] getFlags() { 
         return new boolean[]{false, false, false, false, true };
     }
     public State next() { return null;}
  } 

使用此状态显示对话框

new MyDialog(showOptionsTable, new InitialState() );

...

当应用程序的状态发生变化时,您将更改State对象。

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() );