有时条件可能会变得相当复杂,所以为了便于阅读,我通常将它们拆分并给每个组件一个有意义的名称。然而,这会影响短路评估,这会造成问题。我想出了一个封装方法,但在我看来,它太冗长了。如何分解复杂的条件并保持短路评估?
任何人都可以想出一个整洁的解决方案呢?
为我的意思的例子参见下面的代码:
public class BooleanEvaluator {
// problem: complex boolean expression, hard to read
public static void main1(String[] args) {
if (args != null && args.length == 2 && !args[0].equals(args[1])) {
System.out.println("Args are ok");
}
}
// solution: simplified by splitting up and using meaningful names
// problem: no short circuit evaluation
public static void main2(String[] args) {
boolean argsNotNull = args != null;
boolean argsLengthOk = args.length == 2;
boolean argsAreNotEqual = !args[0].equals(args[1]);
if (argsNotNull && argsLengthOk && argsAreNotEqual) {
System.out.println("Args are ok");
}
}
// solution: wrappers to delay the evaluation
// problem: verbose
public static void main3(final String[] args) {
abstract class BooleanExpression {
abstract boolean eval();
}
BooleanExpression argsNotNull = new BooleanExpression() {
boolean eval() {
return args != null;
}
};
BooleanExpression argsLengthIsOk = new BooleanExpression() {
boolean eval() {
return args.length == 2;
}
};
BooleanExpression argsAreNotEqual = new BooleanExpression() {
boolean eval() {
return !args[0].equals(args[1]);
}
};
if (argsNotNull.eval() && argsLengthIsOk.eval() && argsAreNotEqual.eval()) {
System.out.println("Args are ok");
}
}
}
应对答案:
感谢您的想法!到目前为止,有以下两个选项提交:
- 断裂线,并添加注释
- 保留原样
- 提取方法
- 早期回报
- 嵌套/分手了,如果是
断行并添加评论:
只需在条件中添加换行符就可以在Eclipse中通过代码格式化程序取消(ctrl + shift + f)。内联注释有助于此,但在每行上留下很小的空间,并可能导致丑陋的包装。但在简单的情况下,这可能就足够了。
保持原样:
我给的例子条件相当简单,所以你可能不会需要解决在这种情况下可读性的问题。我想到的情况下情况是复杂得多,例如:
private boolean targetFound(String target, List<String> items,
int position, int min, int max) {
return ((position >= min && position < max && ((position % 2 == 0 && items
.get(position).equals(target)) || (position % 2 == 1)
&& position > min && items.get(position - 1).equals(target)))
|| (position < min && items.get(0).equals(target)) || (position >= max && items
.get(items.size() - 1).equals(target)));
}
我不会推荐离开这个,因为它是。
提取方法:
我认为提取方法,如在几个答案建议。那缺点是,这些方法通常具有非常低的粒度,也有可能通过自己非常有意义的,所以它会搅乱你的类,例如:
private static boolean lengthOK(String[] args) {
return args.length == 2;
}
这不是真的应该是一个独立的方法在课堂上。您也必须将所有相关参数传递给每个方法。如果您纯粹为了评估非常复杂的情况而创建单独的类,那么这可能是一个好的解决方案IMO。
我试图用BooleanExpression方法实现的是逻辑保持本地。注意,即使是布尔表达式的声明也是本地的(我不认为我以前曾经遇到过本地类声明的用例)。
早期的回报:
早期恢复解决方案似乎足够了,即使我并不喜欢的习语。另一种表示法:
public static boolean areArgsOk(String[] args) {
check_args: {
if (args == null) {
break check_args;
}
if (args.length != 2) {
break check_args;
}
if (args[0].equals(args[1])) {
break check_args;
}
return true;
}
return false;
}
我知道大多数人讨厌的标签和休息,而这种风格可能太罕见考虑可读性。
嵌套/分手,如果是:
它允许引进与优化相结合的评估有意义的名字。缺点是条件语句的复杂的树,可随之而来
摊牌
所以,看哪种方法我utlimately的青睐,我申请了几个建议的解决方案,以上面提出的复杂targetFound例子。下面是我的结果:
嵌套/分,如果的,有意义的名称 非常详细的,有意义的名称不会真正帮助这里的可读性
private boolean targetFound1(String target, List<String> items,
int position, int min, int max) {
boolean result;
boolean inWindow = position >= min && position < max;
if (inWindow) {
boolean foundInEvenPosition = position % 2 == 0
&& items.get(position).equals(target);
if (foundInEvenPosition) {
result = true;
} else {
boolean foundInOddPosition = (position % 2 == 1)
&& position > min
&& items.get(position - 1).equals(target);
result = foundInOddPosition;
}
} else {
boolean beforeWindow = position < min;
if (beforeWindow) {
boolean matchesFirstItem = items.get(0).equals(target);
result = matchesFirstItem;
} else {
boolean afterWindow = position >= max;
if (afterWindow) {
boolean matchesLastItem = items.get(items.size() - 1)
.equals(target);
result = matchesLastItem;
} else {
result = false;
}
}
}
return result;
}
嵌套/分,如果的,有评论 不详细,但仍难以阅读和容易造成错误
private boolean targetFound2(String target, List<String> items,
int position, int min, int max) {
boolean result;
if ((position >= min && position < max)) { // in window
if ((position % 2 == 0 && items.get(position).equals(target))) {
// even position
result = true;
} else { // odd position
result = ((position % 2 == 1) && position > min && items.get(
position - 1).equals(target));
}
} else if ((position < min)) { // before window
result = items.get(0).equals(target);
} else if ((position >= max)) { // after window
result = items.get(items.size() - 1).equals(target);
} else {
result = false;
}
return result;
}
早返回 更加紧凑,但条件树仍然只是复杂
private boolean targetFound3(String target, List<String> items,
int position, int min, int max) {
if ((position >= min && position < max)) { // in window
if ((position % 2 == 0 && items.get(position).equals(target))) {
return true; // even position
} else {
return (position % 2 == 1) && position > min && items.get(
position - 1).equals(target); // odd position
}
} else if ((position < min)) { // before window
return items.get(0).equals(target);
} else if ((position >= max)) { // after window
return items.get(items.size() - 1).equals(target);
} else {
return false;
}
}
提取方法 导致荒谬的方法,你 类传球参数是烦人
private boolean targetFound4(String target, List<String> items,
int position, int min, int max) {
return (foundInWindow(target, items, position, min, max)
|| foundBefore(target, items, position, min) || foundAfter(
target, items, position, max));
}
private boolean foundAfter(String target, List<String> items, int position,
int max) {
return (position >= max && items.get(items.size() - 1).equals(target));
}
private boolean foundBefore(String target, List<String> items,
int position, int min) {
return (position < min && items.get(0).equals(target));
}
private boolean foundInWindow(String target, List<String> items,
int position, int min, int max) {
return (position >= min && position < max && ((position % 2 == 0 && items
.get(position).equals(target)) || (position % 2 == 1)
&& position > min && items.get(position - 1).equals(target)));
}
BooleanExpression重新包装 请注意,方法参数必须声明为最终 这种复杂的情况下冗长是可防御IMO 也许将封闭使这更容易,如果他们对(曾经同意 -
private boolean targetFound5(final String target, final List<String> items,
final int position, final int min, final int max) {
abstract class BooleanExpression {
abstract boolean eval();
}
BooleanExpression foundInWindow = new BooleanExpression() {
boolean eval() {
return position >= min && position < max
&& (foundAtEvenPosition() || foundAtOddPosition());
}
private boolean foundAtEvenPosition() {
return position % 2 == 0 && items.get(position).equals(target);
}
private boolean foundAtOddPosition() {
return position % 2 == 1 && position > min
&& items.get(position - 1).equals(target);
}
};
BooleanExpression foundBefore = new BooleanExpression() {
boolean eval() {
return position < min && items.get(0).equals(target);
}
};
BooleanExpression foundAfter = new BooleanExpression() {
boolean eval() {
return position >= max
&& items.get(items.size() - 1).equals(target);
}
};
return foundInWindow.eval() || foundBefore.eval() || foundAfter.eval();
}
我想这真的取决于形势(一如既往)。对于非常复杂的条件,包装方法可能是可以捍卫的,尽管它并不常见。
感谢您的所有意见!
编辑:后续。这可能是更好的创建复杂的逻辑,一个特定的类像这样:
import java.util.ArrayList;
import java.util.List;
public class IsTargetFoundExpression {
private final String target;
private final List<String> items;
private final int position;
private final int min;
private final int max;
public IsTargetFoundExpression(String target, List<String> items, int position, int min, int max) {
this.target = target;
this.items = new ArrayList(items);
this.position = position;
this.min = min;
this.max = max;
}
public boolean evaluate() {
return foundInWindow() || foundBefore() || foundAfter();
}
private boolean foundInWindow() {
return position >= min && position < max && (foundAtEvenPosition() || foundAtOddPosition());
}
private boolean foundAtEvenPosition() {
return position % 2 == 0 && items.get(position).equals(target);
}
private boolean foundAtOddPosition() {
return position % 2 == 1 && position > min && items.get(position - 1).equals(target);
}
private boolean foundBefore() {
return position < min && items.get(0).equals(target);
}
private boolean foundAfter() {
return position >= max && items.get(items.size() - 1).equals(target);
}
}
的逻辑是足以保证一个单独的类复杂(单元测试,耶!)。它将使使用此逻辑的代码更具可读性,并在其他地方需要此逻辑的情况下促进重用。我认为这是一个很好的课,因为它真的有单一的责任,只有最后的领域。
我喜欢你的第一个解决方案,但是通过适当的变量命名,它可能不是必需的。你的第二个解决方案太冗长的IMO。 – BacMan 2009-12-18 15:10:20