2011-09-08 1 views
19

thể trùng lặp:
Long list of if statements in JavaLàm thế nào để loại bỏ lớn if-else-nếu chuỗi

Tôi được giao nhiệm vụ để làm việc với một số mã, và có một người khổng lồ if-else-nếu chain (100+ else-ifs) kiểm tra chuỗi.

Một số kỹ thuật tốt để cập nhật mã này là nơi mà chuỗi if-else-if có thể được thu nhỏ thành một thứ dễ quản lý hơn.

Chuỗi trông giống như sau:

if(name.equals("abc")){ 
    do something 
} else if(name.equals("xyz")){ 
    do something different 
} else if(name.equals("mno")){ 
    do something different 
} ...... 
..... 
else{ 
    error 
} 
+1

Các mô hình lệnh là cách để xử lý này cho chắc chắn, xem: http://stackoverflow.com/questions/1199646/long-list-of-if-statements-in-java/1199677#1199677 –

+0

Chính xác điều gì là sai với nó như là, khác hơn là dường như khó chịu? –

Trả lời

16

Bạn có thể trích xuất mã trong mỗi nhánh thành một phương thức riêng biệt, sau đó chuyển các phương thức thành các triển khai của một giao diện cơ sở chung (hãy gọi nó là Handler). Sau đó, bạn có thể điền vào một Map<String, Handler> và chỉ cần tra cứu và thực thi trình xử lý phù hợp cho chuỗi đã cho.

Thật không may việc thực hiện hơn 100 lớp con cho giao diện yêu cầu khá nhiều mã soạn sẵn, nhưng hiện tại không có cách nào đơn giản hơn trong Java để đạt được điều này. Việc triển khai các trường hợp dưới dạng các thành phần của Enum có thể giúp phần nào - here is an example. Các giải pháp lý tưởng sẽ được sử dụng closures/lambdas, nhưng chúng tôi phải chờ cho đến khi Java 8 cho rằng ...

+0

+1 Đó là cách tiếp cận thông thường của tôi. Làm cho mã sạch và dễ mở rộng. Khi Java được lambdas nó sẽ làm cho nó đẹp. – linuxuser27

+0

Điều gì về thực tế là bạn cần giữ nhiều phiên bản Handler trong Bản đồ có thể sẽ không được sử dụng? – Galya

+0

Bạn có đo lường điều này với JMH không? Nó sẽ được tốt đẹp để biết tại thời điểm những gì chi phí của [năng động dispatch] (http://shipilev.net/blog/2015/black-magic-method-dispatch/) là thấp hơn so với if-else branching. –

2

bạn có thể sử dụng câu lệnh switch, nhưng Chuyển sang báo cáo với trường hợp chuỗi đã được thực hiện trong Java SE 7

giải pháp tốt nhất là sử dụng command pattern

+1

nó không phải là một ý tưởng tồi nhưng làm cho một mô hình của lệnh tôi nghĩ rằng nên là một giải pháp tốt hơn – Jorge

6

Giống như Matt Ball cho biết trong bình luận của mình, bạn có thể sử dụng một mẫu lệnh. Xác định một tập hợp các lớp Runnable:

Runnable task1 = new Runnable() { 
    public void run() { /* do something */ } 
}; 
Runnable task2 = // etc. 

Sau đó, bạn có thể sử dụng một bản đồ từ khóa của bạn để Runnables:

Map<String,Runnable> taskMap = new HashMap<String,Runnable>(); 
taskMap.put("abc", task1); 
taskMap.put("xyz", task2); 
// etc. 

Cuối cùng, thay thế if-else chuỗi với:

Runnable task = taskMap.get(name); 
if (task != null) { 
    task.run(); 
} else { 
    // default else action from your original chain 
} 
0

Đây là một phổ biến Arrow Anti-Pattern và Jeff thảo luận về một số phương pháp tiếp cận để xử lý này rất độc đáo trong bài viết của mình here.

7

Một số tùy chọn/ý tưởng:

  • Để lại nó vì nó là - nó không bị hỏng về cơ bản, và là một cách hợp lý rõ ràng và đơn giản để duy trì
  • Sử dụng một câu lệnh switch (nếu bạn đang sử dụng Java 7) - không chắc chắn điều này có giúp bạn nhiều hay không
  • Tạo một HashMap của chuỗi thành FunctionObjects nơi các đối tượng hàm thực hiện hành vi bắt buộc làm phương thức. Sau đó, mã gọi điện của bạn chỉ là: hashMap.get(name).doSomething();
  • Chia nhỏ mã thành một chế độ theo dõi của các cuộc gọi hàm bằng cách phân nhóm các chuỗi.Bạn có thể làm điều này bằng cách lấy từng chữ cái, vì vậy một chi nhánh xử lý tất cả các tên bắt đầu bằng 'a', v.v.
  • Refactor để bạn không chuyển tên. Sau đó, bạn chỉ có thể thực hiện namedObject.doSomething()
8

Với Enums, bạn có thể có phương pháp cho mỗi trường hợp.

public enum ActionEnum { 
    ABC { 
     @Override 
     void doSomething() { 
      System.out.println("Doing something for ABC");  
     } 

    }, 
    XYZ { 
     @Override 
     void doSomething() { 
     System.out.println("Doing something for XYZ"); 
     } 
    }; 

    abstract void doSomething(); 
} 

public class MyActionClass { 

    public void myMethod(String name) { 
     ActionEnum.valueOf("ABC").doSomething(); 
    } 

} 

Nó vẫn còn kinda lộn xộn (enum lớn với hơn 100 mục, thậm chí nó tất cả nó được cử), nhưng có thể tránh mã HashMap khởi (100 puts cũng là lộn xộn theo ý kiến ​​của tôi).

Và thêm một tùy chọn (cho mục đích tài liệu) sẽ được phản ánh:

public interface Action { 
    void doSomething(); 
} 

public class ABCAction implements Action { 
    @Override 
    public void doSomething() { 
     System.out.println("Doing something for ABC");  
    } 
} 

public class MyActionClass { 

    void doSomethingWithReflection(String name) { 
     try { 
      Class<? extends Action> actionClass = Class. 
       forName("actpck."+ name + "Action").asSubclass(Action.class); 
      Action a = actionClass.newInstance(); 
      a.doSomething(); 
     } catch (Exception e) { 
      // TODO Catch exceptions individually and do something useful. 
      e.printStackTrace(); 
     } 
    } 
} 

Mỗi phương pháp đều có đó là sự đánh đổi thương mại:

  • HashMap = nhanh + Kinda lộn xộn ("set-up" mã với hàng trăm lượt đặt)
  • Enum = Fast + Kinda lộn xộn 2 (tệp lớn).
  • Phản ánh = Dễ bị lỗi hơn + lỗi thời gian chạy, nhưng cung cấp sự tách biệt rõ ràng mà không cần phải sử dụng HashMap lớn clunky.
+0

Phân tích đẹp, cảm ơn! – bertie