我有一个对话框,根据应用程序的状态,当前用户的安全性等显示各种内容。 我目前正在传递几个布尔标志,然后根据这些标志启用和/或隐藏UI组件.Eg:
new MyDialog(showOptionsTable, allowFooInput, allowBarInput, isSuperUser)
最初,这开始是一些标志,这很好。但是现在随着需求的变化,它已经演变为五个布尔标志的输入。
处理此类行为的最佳做法是什么?这是否应根据对话框的外观进行子类化?
答案 0 :(得分:10)
与许多事情一样,“这取决于”。
Ben Noland建议一个类来保存配置选项。这是可行的,但有利于不变性,并可选择使用构建器模式。因为布尔值是内置类型,所以编写一个小型构建器将真正帮助人们理解代码。如果你将它与MyDialog(真实,真实,......)进行比较,你知道我的意思:
Options.allowThis()。allowThat()。建立()
Chris提出了比特字段,但正如一些评论者指出的那样,由于Josh Bloch的Effective Java中列出的许多原因,比特字段是邪恶的。基本上他们很难调试和容易出错(你可以传入任何int,它仍然会编译)。因此,如果你走这条路线,请使用真实的枚举和EnumSet。
答案 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() );