2013-08-30 17 views
8

Khi dòng mã là như thế này:Một số cách tốt hơn để tránh làm gì trong khi (sai); hack trong Java?

if(check()) 
{ 
    ... 
    ... 
    if(check()) 
    { 
    ... 
    ... 
    if(check()) 
    { 
     ... 
     ... 
    } 
    } 
} 

tôi đã thường thấy công việc này xung quanh để tránh dòng chảy này mã lộn xộn:

do { 
    if(!check()) break; 
    ... 
    ... 
    if(!check()) break; 
    ... 
    ... 
    if(!check()) break; 
    ... 
    ... 
} while(false); 

một số cách tốt hơn mà tránh workaround này là gì/hack để rằng nó sẽ trở thành mã cấp cao hơn (cấp độ ngành)?

Có thể có các cấu trúc đến từ cộng đồng Apache hoặc Google ổi không?

Lưu ý: đây là bản sao của the same question for C++. Các câu trả lời tốt nhất có các con trỏ hàm thực sự và lệnh GOTO. Cả hai đều không tồn tại trong Java. Tôi háo hức quan tâm đến điều tương tự cho Java.

Đưa nó vào một chức năng mới và sử dụng trả lại là ý kiến ​​của tôi không phải là một giải pháp tốt, bởi vì trả lại thoát khỏi phương pháp. Vì vậy, nếu lớp của tôi có 20 phương pháp với các cấu trúc này, tôi sẽ phải thêm 20 phương thức bổ sung để thực hiện việc này. Đó là lý do tại sao GOTO là câu trả lời hay nhất cho C++.

+6

Một số các câu trả lời vẫn giữ - di chuyển nó vào chức năng riêng của nó, và sử dụng trở lại ví dụ. – Sinkingpoint

+0

@MarounMaroun Bởi vì bạn có thể có 20 lần này trong một hàm để vượt qua 20 bước nối tiếp. Nếu tôi làm như thế này, cuối cùng nếu các trang trình bày đã có 20 tab ở bên phải, điều này làm cho mã không thể đọc được. –

+3

Tôi không phải là một người tôn giáo, nhưng tôi thực sự tin rằng "GOTO" không phải là câu trả lời đúng cho bất cứ điều gì liên quan đến Java. – WarrenFaith

Trả lời

6

Điều này "trong khi (sai)" vô nghĩa? Sử dụng nhãn!

myLabel: { 
    if(!check()) break myLabel; 
    ... 
    ... 
    if(!check()) break myLabel; 
    ... 
    ... 
    if(!check()) break myLabel; 
    ... 
} 

Đó là cốt lõi Java: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html

+1

Tôi chấp nhận điều này, vì nó gần giống với nguồn gốc. Tất nhiên các câu trả lời khác cũng khá tốt. Điều này giải quyết chính xác vấn đề mà người dùng C++ (và Java) có và rõ ràng là có một giải pháp rất dễ dàng trong Java. Cảm ơn sự đóng góp này. –

+1

Bạn được chào đón. – mvmn

7

Để giảm cyclomatic complexity, bạn có thể tách logic vào submethods:

public void go() { 
    first(); 
} 

public void first() { 
    if(check()) { 
     // #1 
     second(); 
    } 
} 

public void second() { 
    if(check()) { 
     // #2 
     third(); 
    } 
} 

public void third() { 
    if(check()) { 
     // #3 
    } 
} 
+0

Cảm ơn câu trả lời của bạn. Vâng, bạn đúng về sự phức tạp. Tuy nhiên, trong câu hỏi này, trọng tâm là nhiều hơn về khả năng đọc và làm đẹp mã. Tốc độ không phải là mục tiêu chính. Tôi cho rằng dù sao thì các trình biên dịch JIT hiện đại sẽ xử lý tốt. –

2

Bởi vì bạn có thể có điều này 20 lần trong một chức năng để vượt qua 20 bước sê-ri. Nếu tôi làm như thế này, cuối cùng nếu các trang trình bày đã có 20 tab ở bên phải, điều này làm cho mã không thể đọc được.

Nhận xét này của bạn cho thấy sự cố: bạn đang tưởng tượng cấu trúc mã sai.

Ví dụ mã của bạn là như thế này

if (bool) { 
    // do something 
    if (anotherBoolean) { 
     //do even more 
     if (thirdBoolean) { 
      // here we go! I spare deeper structure... 
     } 
    } 
} 

này có thể được refactored dễ dàng để:

public void method() { 
    if (bool) { 
     doSomeStuff(); 
    } 
} 

public void doSomeStuff() { 
    if (anotherBoolean) { 
     doThirdStuff(); 
    } 
} 

public void doThirdStuff() { 
    if (thirdBoolean) { 
     doEvenMore(); 
    } 
} 

public void doEvenMore() { 
    // deeper structure 
} 

Đó là loại cấu trúc mã sẽ là một mẫu tốt của Bộ luật Sạch như các phương pháp đang làm công cụ "mục đích duy nhất" của họ, bạn không thực hiện hack trong khi cộng với bạn thậm chí có thể sử dụng lại các phương thức ở đâu đó.

+0

Cảm ơn sự đóng góp này. Trong trường hợp của tôi, tôi có 4 phương pháp trong lớp của tôi và họ chạy trên 6 booleans. Vì vậy, tôi sẽ phải thêm 24 phương pháp vào lớp. Bây giờ hãy thử tìm 24 tên khác nhau cho các phương thức. Được rồi, tôi phát triển, điều đó phụ thuộc vào mã thực sự của tôi. Thay vì tìm thêm tên, tôi có thể thêm các tham số vv Vì vậy, giải pháp của bạn có thể làm việc cho các trường hợp cụ thể. Tôi nghĩ ai đó có thể có một cái gì đó mát mẻ trong tâm trí như chuyển đổi, ngoại lệ bắt hoặc tĩnh nhập khẩu. Bất cứ điều gì khác ngoài việc tạo ra các phương pháp mới. –

+1

Nếu 6 boolean của bạn đại diện cho một số loại trạng thái, bạn có thể thử enum và sử dụng chúng trong một tuyên bố trường hợp chuyển đổi. Tôi sử dụng nó để xử lý đầu vào, ví dụ. State.PRESSED, RELEASE, MOVING, NONE vv và các phương thức gọi theo trạng thái đó. – WarrenFaith

-1

Đây có thể là một giải pháp:

public interface Stuff { 
    void run(); 
} 

private List<Stuff> stuffs; 

// logical parts 
public void init() { 
    Stuff first = new Stuff() { 
     public void run() { 
      // stuff #1 
     } 
    }; 
    Stuff second = new Stuff() { 
     public void run() { 
      // stuff #2 
     } 
    }; 
    Stuff third = new Stuff() { 
     public void run() { 
      // stuff #3 
     } 
    }; 
    // LinkedList to keep order 
    stuffs = new LinkedList<Stuff>(Arrays.asList(first, second, third)); 
} 

// main method, just launch the stuffs while check() returns true 
// no change needed when new stuff 
public void go() { 
    for (Stuff stuff : stuffs) { 
     if (!check()) break; 
     stuff.run(); 
    } 
} 

Nếu chất liệu của bạn có thể có điều kiện khác nhau để kiểm tra, bạn có thể cải thiện với:

public interface Stuff { 
    void run(); 
    boolean condition(); 
} 

private List<Stuff> stuffs; 

public void init() { 
    Stuff first = new Stuff() { 
     public void run() { 
      // stuff #1 
     } 
     public boolean condition() { 
      return true; // condition to call this run() 
     } 
    }; 
    Stuff second = new Stuff() { 
     public void run() { 
      // stuff #2 
     } 
     public boolean condition() { 
      return false; // condition to call this run() 
     } 
    }; 
    Stuff third = new Stuff() { 
     public void run() { 
      // stuff #3 
     } 
     public boolean condition() { 
      return true; // condition to call this run() 
     } 
    }; 
    stuffs = new LinkedList<Stuff>(Arrays.asList(first, second, third)); 
} 

public void go() { 
    for (Stuff stuff : stuffs) { 
     if (!stuff.condition()) break; 
     stuff.run(); 
    } 
} 
+0

+1 điều này thực sự tuyệt vời. –

+3

-1 Xin lỗi. Nó cho đến nay có thể đọc được nó buồn. So sánh mã này với mã tái cấu trúc quặng gốc của OP. Mã OP là waaaay rõ ràng hơn. Câu trả lời này là một sự tò mò và một mô hình chống. – Bohemian

+1

Tôi không thấy một mẫu chống trong này. Âm thanh populistly với tôi. Mã nguồn đầu tiên của ví dụ này là cho tôi mẫu OOP lý tưởng cho vấn đề này. Dù sao, mã nguồn thứ hai, đã được thêm vào đó để mở rộng câu trả lời, dường như gây nhầm lẫn cho mọi người và bây giờ trông nó giống như một câu trả lời tồi tệ, điều đó làm quá tải vấn đề đơn giản ban đầu. –

0

Đó là một câu hỏi hay. Ngữ nghĩa của bất kỳ giải pháp nào phải liên quan đến một số loại khối dễ bị kích động, theo nghĩa đen hoặc ngụ ý bằng cách thực hiện có thể được thoát ra.

Một số các câu trả lời khác rất phức tạp mà họ trong khi họ đạt được tránh do-while loop, mất tất cả rõ ràng và dễ đọc, mà làm cho họ "xấu" mã.

Sau một vài suy nghĩ, đây là cách tôi sẽ mã nó:

doSomething(); // Replace current code with a method call 

private void doSomething() { 
    if(!check()) return; 
    ... 
    ... 
    if(!check()) return; 
    ... 
    ... 
    if(!check()) return; 
    ... 
    ... 
} 

điểm chính:

  • Một phương pháp là một khối, được sạch thoát bởi return
  • Ba phần chức năng rõ ràng là một đơn vị lập trình, do đó, chúng cùng nhau là một ứng cử viên chuỗi để tái cấu trúc thành một phương pháp riêng biệt
  • Không có "contrivances", chẳng hạn như perfunct ory do-while (false) xây dựng, mà chỉ tồn tại để cung cấp một khối exitable và có thể nhầm lẫn giữa những người khác
  • Bằng cách sử dụng các bài kiểm tra tiêu cực sau đó thoát, có hai điều đang đạt được:
    • các "exit sớm" mã hóa mô
    • ít thụt đầu dòng/làm tổ, làm giảm các cyclic complexity
  • Không khối tắm/lớp/đối tượng được tạo ra, duy trì khả năng đọc và hiệu suất (mặc dù một khung sẽ được đẩy vào stack cho các cuộc gọi phương pháp - một tác động không đáng kể
+0

Huh? Downvote? Chăm sóc để chia sẻ lý do tại sao? Cách tiếp cận này có vẻ ổn với tôi (thậm chí tốt). – Bohemian