有時條件可能會變得相當複雜,所以爲了便於閱讀,我通常將它們拆分並給每個組件一個有意義的名稱。然而,這會影響短路評估,這會造成問題。我想出了一個封裝方法,但在我看來,它太冗長了。如何分解複雜的條件並保持短路評估?
任何人都可以想出一個整潔的解決方案呢?
爲我的意思的例子參見下面的代碼:
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