2016-11-29 18 views
8

Tôi có một tình huống mà tôi cần kiểm tra nhiều điều kiện, trong đó mỗi kết hợp có một kết quả khác nhau. Trong điều kiện cụ thể của tôi, tôi đã có 2 biến, đó là các loại enum, mà mỗi có thể là 2 giá trị khác nhau.Kiểm tra hiệu quả nhiều điều kiện

enum Enum1 
{ 
    COND_1, 
    COND_2 
} 
enum EnumA 
{ 
    COND_A, 
    COND_B 
} 
Enum1 var1; 
EnumA varA; 

Điều này mang lại cho tôi 4 điều kiện có thể, yêu cầu 4 kết quả khác nhau. Tôi đã đưa ra một vài cách khác nhau để làm điều này, hoặc là sử dụng nếu báo cáo hoặc báo cáo chuyển đổi:

if(var1 == Enum1.COND_1 && varA == EnumA.COND_A) 
{ 
    // Code 
} 
else if(var1 == Enum1.COND_1 && varA == EnumA.COND_B) 
{ 
    // Code 
} 
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_A) 
{ 
    // Code 
} 
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_B) 
{ 
    // Code 
} 

Hoặc:

switch(var1) 
{ 
    case COND_1: 
     switch(varA) 
     { 
      case COND_A: 
       // Code 
       break; 
      case COND_B: 
       // Code 
       break; 
     } 
     break; 
    case COND_2: 
     switch(varA) 
     { 
      case COND_A: 
       // Code 
       break; 
      case COND_B: 
       // Code 
       break; 
     } 
     break; 
} 

Tôi đã suy nghĩ của người khác, nhưng không muốn để điền vào mã này: P Tôi muốn biết cách tốt nhất để làm điều này là gì. Tôi nghĩ rằng chuyển đổi dễ đọc hơn một chút, nhưng các ifs ngắn hơn. Tôi nghĩ nó sẽ rất tuyệt nếu các công tắc có thể có nhiều điều kiện, nhưng tôi chưa từng nghe về nó. Điều này cũng đặt ra câu hỏi: cách tốt nhất để làm điều này với một số tùy ý các biến và giá trị có thể là gì?

+2

Nếu bạn đang thực hiện việc này theo cách * nghiêm túc *, bạn có thể sử dụng công cụ quy tắc hoặc hệ thống bên thứ ba khác sẽ trừu tượng tất cả mã đó. – Kayaman

+1

Tại sao bạn cần kiểm tra nhiều điều kiện như vậy? Bạn đang thực sự làm gì? Điều này có thể được giải quyết một cách đẹp hơn với thiết kế lại thay vì cố gắng quyết định giữa ifs và switch. – Kayaman

+0

https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern –

Trả lời

3

Tôi thích biến thể if không có lồng nhau vì nó ngắn và bạn có tất cả các điều kiện trong một dòng.

Khi dừng qua mã trong quá trình gỡ lỗi, nó có thể trở nên tẻ nhạt, vì bạn phải bước qua tất cả các điều kiện trước, đó là O (n). Khi thực thi mã, điều này không quan trọng vì trình biên dịch có thể sẽ tối ưu hóa mã.

Không có cách nào rõ ràng nhất, vì vậy bạn sẽ phải thử nghiệm một chút.

1

Có thể là ý tưởng điên rồ nhưng bạn có thể xây dựng một int hoặc một byte bằng cách sử dụng cờ và sử dụng nó trong một chuyển đổi duy nhất.

private int getIntegerStateForConditions(boolean... conditions){ 
    int state = 0; 
    int position = 0; 
    for(boolean condition: conditions){ 
     if(condition){ 
      state = state || (1 << position++); 
     } 
    } 
    return state; 
} 

...

switch(getIntegerStateForCondition((var1 == Enum1.COND_1), (var2 == EnumA.COND_A)){ 
    case 0: ... //both condition false 
    case 1: ... //first condition true second false 
    case 2: ... //first false, second true ... 
} 

...

Tôi nghĩ rằng đây là rất xa là mã sạch sẽ nhưng có vẻ tốt hơn.

1

Nếu tôi là bạn, tôi sẽ dựa vào cờ bit để chỉ có một byte (vì bạn chỉ có 4 trường hợp sử dụng) để xử lý và sử dụng tuyên bố switch trên byte này để quản lý tất cả các trường hợp sử dụng của bạn.

Something như thế này:

private static final int COND_2 = 1; 
private static final int COND_B = 2; 

private byte value; 

public void setValue(Enum1 enum1) { 
    if (enum1 == Enum1.COND_1) { 
     this.value &= ~COND_2; 
    } else { 
     this.value |= COND_2; 
    } 
} 

public void setValue(EnumA enumA) { 
    if (enumA == EnumA.COND_A) { 
     this.value &= ~COND_B; 
    } else { 
     this.value |= COND_B; 
    } 
} 

public Enum1 getEnum1() { 
    return (this.value & COND_2) == COND_2 ? Enum1.COND_2 : Enum1.COND_1; 
} 


public EnumA getEnumA() { 
    return (this.value & COND_B) == COND_B ? EnumA.COND_B : EnumA.COND_A; 
} 

Sau đó, kiểm tra của bạn sẽ là:

switch (value) { 
    case 0 : 
     // 1-A; 
     break; 
    case 1 : 
     // 2-A; 
     break; 
    case 2 : 
     // 1-B; 
     break; 
    case 3 : 
     // 2-B; 
     break; 
} 
9

Đối với trường hợp sử dụng nhỏ của bạn tôi có lẽ sẽ đi cho lồng nhau if báo cáo. Nhưng nếu bạn có nhiều hằng số enum, có lẽ một mẫu sử dụng luồng có thể làm cho mã của bạn dễ đọc và duy trì hơn (đối với một hình phạt hiệu suất nhỏ).Bạn có thể giải quyết nó bằng một dòng như thế này:

Stream.of(new Conditional(COND_1, COND_A,() -> {/* do something */}), 
      new Conditional(COND_1, COND_B,() -> {/* do something */}), 
      new Conditional(COND_2, COND_A,() -> {/* do something */}), 
      new Conditional(COND_2, COND_B,() -> {/* do something */})) 
     .filter(x -> x.test(var1, varA)) 
     .findAny() 
     .ifPresent(Conditional::run); 

Điều đó sẽ đòi hỏi một lớp hỗ trợ:

class Conditional implements BiPredicate<Enum1, EnumA>, Runnable 
{ 
    private final Enum1 var1; 
    private final EnumA varA; 
    private final Runnable runnable; 

    public Conditional(Enum1 var1, EnumA varA, Runnable runnable) { 
     this.var1 = var1; 
     this.varA = varA; 
     this.runnable = runnable; 
    } 

    @Override 
    public boolean test(Enum1 enum1, EnumA enumA) { 
     return var1 == enum1 && varA == enumA; 
    } 

    @Override 
    public void run() { 
     runnable.run(); 
    } 
} 
1

tôi sẽ đích thân thích này:

if(understandableNameInContextName1(var1, varA)) 
{ 
    // Code 
} 
else if(understandableNameInContextName2(var1, varA)) 
{ 
    // Code 
} 
else if(understandableNameInContextName3(var1, varA)) 
{ 
    // Code 
} 
else if(understandableNameInContextName4(var1, varA)) 
{ 
    // Code 
} 

private boolean understandableNameInContextName1(Object var1, Object varA){ 
return (var1 == Enum1.COND_1 && varA == EnumA.COND_A); 
} 

private boolean understandableNameInContextName2(Object var1, Object varA){ 
return (var1 == Enum1.COND_1 && varA == EnumA.COND_B); 
} 

private boolean understandableNameInContextName3(Object var1, Object varA){ 
return (var1 == Enum1.COND_2 && varA == EnumA.COND_A); 
} 

private boolean understandableNameInContextName4(Object var1, Object varA){ 
return (var1 == Enum1.COND_2 && varA == EnumA.COND_B); 
} 

Và tên của các phương pháp có thể được như thế, isOrderShippedAndDelivered(), isRequestSendAndAckRecieved().

Lý do là điều này sẽ làm cho mã dễ đọc hơn nhiều. Trừ khi bạn có dữ liệu đưa bạn trở lại những điều này nếu tuyên bố sẽ không đạt được nhiều tối ưu hóa những điều này.

Xem: https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil

7

khác biệt hiệu suất có lẽ là không đáng kể ở đây, vì vậy tôi sẽ tập trung vào phần lông ngắn và dễ đọc. Vì vậy, tôi chỉ có thể đơn giản hóa 's if một chút bằng cách sử dụng các biến tạm thời:

boolean is_1 = (var1 == Enum1.COND_1); 
boolean is_A = (varA == EnumA.COND_A); 

if(is_1 && is_A) 
{ 
    // Code 
} 
else if(is_1 && !is_A) 
{ 
    // Code 
} 
else if(!is_1 && is_A) 
{ 
    // Code 
} 
else if(!is_1 && !is_A) 
{ 
    // Code 
} 
3

Tôi chắc chắn thích phiên bản phẳng, nó chỉ có thể sử dụng một chút ít sự trùng lặp:

// If you can't make the variables final, make some final copies 
final Enum1 var1 = Enum1.COND_2; 
final EnumA varA = EnumA.COND_B; 

class Tester { // You could also make an anonymous BiPredicate<Enum1, EnumA> 
    boolean t(Enum1 v1, EnumA vA) { 
     return var1 == v1 && varA == vA; 
    } 
}; 

Tester tes = new Tester(); 

if (tes.t(Enum1.COND_1, EnumA.COND_A)) { 
    // code 
} else if (tes.t(Enum1.COND_1, EnumA.COND_B)) { 
    // code 
} else if (tes.t(Enum1.COND_2, EnumA.COND_A)) { 
    // code 
} else if (tes.t(Enum1.COND_2, EnumA.COND_B)) { 
    // code 
} 

Run nó here. Bạn có thể làm cho nó ngắn hơn và ít dư thừa hơn bằng cách thực hiện static import of the enums để tránh đề cập đến tên enum, ví dụ: tes.t(COND_1, COND_B). Hoặc nếu bạn sẵn sàng từ bỏ một số thời gian an toàn biên dịch, bạn có thể chuyển một chuỗi được chuyển đổi thành hai giá trị enum, ví dụ: tes.t("COND_1 COND_A") (việc triển khai được để lại cho người đọc).

0

Loại phụ thuộc vào độ phức tạp của mã và số kết hợp nhưng tùy chọn khác là từ điển có khóa bao gồm Tuple liệt kê của bạn và giá trị của đại biểu cho mã.

Các vấn đề liên quan