Danh sách dài các câu lệnh if trong Java


101

Xin lỗi, tôi không thể tìm thấy câu hỏi nào trả lời câu hỏi này, tôi gần như chắc chắn rằng ai đó đã nêu câu hỏi này trước đây.

Vấn đề của tôi là tôi đang viết một số thư viện hệ thống để chạy các thiết bị nhúng. Tôi có các lệnh có thể được gửi đến các thiết bị này qua chương trình phát thanh. Điều này chỉ có thể được thực hiện bằng văn bản. bên trong các thư viện hệ thống, tôi có một chuỗi xử lý các lệnh trông như thế này

if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() } 
else if etc. 

Vấn đề là có rất nhiều lệnh nó sẽ nhanh chóng đi đến một thứ gì đó ngoài tầm kiểm soát. Kinh khủng khi nhìn ra ngoài, đau đớn khi gỡ lỗi và tâm trí hoang mang để hiểu trong thời gian vài tháng.


16
Chỉ là một nhận xét - tôi thực sự khuyên bạn nên chọn cuốn sách Gang of Four pattern, hoặc nếu bạn chưa quen với các mẫu, cuốn Head First Design Patterns in Java (cuốn sách này khá dễ đọc và giới thiệu tuyệt vời về một số mẫu phổ biến ). Cả hai đều là tài nguyên quý giá, và cả hai đều đã giúp tôi nhiều hơn một lần.
aperkins,

2
Có thực sự tôi sở hữu chúng nhưng chúng bị thiếu :) Đó là lý do tại sao tôi chắc chắn những gì tôi đã làm là sai :) Không thể tìm thấy một giải pháp chính xác mặc dù! Có lẽ điều này được một vị trí đẹp google
Steve

2
Nó chỉ là Mẫu Lệnh Thứ Hai ở đây!
Nick Veys

Câu trả lời:


171

sử dụng mẫu lệnh :

public interface Command {
     void exec();
}

public class CommandA() implements Command {

     void exec() {
          // ... 
     }
}

// etc etc

sau đó xây dựng một Map<String,Command>đối tượng và điền nó với các Commandphiên bản:

commandMap.put("A", new CommandA());
commandMap.put("B", new CommandB());

thì bạn có thể thay chuỗi if / else if của mình bằng:

commandMap.get(value).exec();

BIÊN TẬP

bạn cũng có thể thêm các lệnh đặc biệt như UnknownCommandhoặc NullCommand, nhưng bạn cần một lệnh CommandMapxử lý các trường hợp góc này để giảm thiểu việc kiểm tra của khách hàng.


1
... với việc kiểm tra thích hợp commandMap.get () không trả về null :-)
Brian Agnew

3
tất nhiên, tôi đã bỏ qua một số mã soạn sẵn vì lợi ích của đơn giản
dfa

10
Thay vì HashMap, bạn có thể sử dụng Java enum, nó cung cấp cho bạn một tập hợp các lệnh được xác định rõ ràng thay vì một bản đồ ủy mị. Bạn có thể có một getter trong enum: Command getCommand (); hoặc thậm chí thực hiện thực thi () như một phương thức trừu tượng trong enum, mà mỗi cá thể thực hiện (enum dưới dạng lệnh).
JeeBee 29/07/09

2
điều này sẽ buộc phải thực hiện tất cả các lệnh trong enum ... điều đó là lý tưởng. Với một giao diện, bạn cũng có thể áp dụng mẫu Decorator (ví dụ: DebugCommandDecorator, TraceCommandDecorator), có rất nhiều tính linh hoạt được tích hợp trong một giao diện Java đơn giản
dfa 29/07/09

5
Được rồi, đối với những tập lệnh nhỏ và không bao giờ phát triển, enum là một giải pháp khả thi.
dfa 29-07-09

12

Đề xuất của tôi sẽ là một kiểu kết hợp nhẹ giữa enum và đối tượng Command. Đây là một thành ngữ được đề xuất bởi Joshua Bloch trong Mục 30 của Java hiệu quả.

public enum Command{
  A{public void doCommand(){
      // Implementation for A
    }
  },
  B{public void doCommand(){
      // Implementation for B
    }
  },
  C{public void doCommand(){
      // Implementation for C
    }
  };
  public abstract void doCommand();
}

Tất nhiên, bạn có thể truyền các tham số cho doCommand hoặc có các kiểu trả về.

Giải pháp này có thể không thực sự phù hợp nếu việc triển khai doCommand không thực sự "phù hợp" với kiểu enum, điều này - như thường lệ khi bạn phải đánh đổi - hơi mờ.


7

Có một enum các lệnh:

public enum Commands { A, B, C; }
...

Command command = Commands.valueOf(value);

switch (command) {
    case A: doCommandA(); break;
    case B: doCommandB(); break;
    case C: doCommandC(); break;
}

Nếu bạn có nhiều hơn một vài lệnh, hãy xem xét việc sử dụng Mẫu lệnh, như đã trả lời ở phần khác (mặc dù bạn có thể giữ lại enum và nhúng lệnh gọi vào lớp thực thi trong enum, thay vì sử dụng HashMap). Vui lòng xem câu trả lời của Andreas hoặc jens 'cho câu hỏi này để làm ví dụ.


5
cho mỗi lệnh mới bạn thêm, bạn cần phải chỉnh sửa chuyển đổi: mã này không tuân theo nguyên tắc mở / đóng
DFA

Phụ thuộc vào việc các lệnh đó là ít hay nhiều phải không? Ngoài ra, trang web này chậm kinh khủng trong những ngày này, phải mất 5 lần thử để chỉnh sửa một câu trả lời.
JeeBee 29/07/09

đây không phải là tối ưu, hãy xem stackoverflow.com/questions/1199646/… về cách thực hiện điều này tối ưu hơn.
Andreas Petersson

Vâng, cảm ơn bạn đã dành thời gian để triển khai những gì tôi đã viết ở cuối nhận xét của mình - Java Enum dưới dạng Command Pattern. Nếu tôi có thể chỉnh sửa bài đăng của mình, tôi sẽ đề cập đến vấn đề này, nhưng trang web này đang chết.
JeeBee

Tôi nghĩ rằng những câu hỏi này đang đòi hỏi một tuyên bố Switch!
Michael Brown,

7

Việc triển khai một giao diện như được trình bày đơn giản và dễ hiểu bởi dfa là rõ ràng và trang nhã (và cách được hỗ trợ "chính thức"). Đây là ý nghĩa của khái niệm giao diện.

Trong C #, chúng ta có thể sử dụng các đại diện cho các lập trình viên thích sử dụng con trỏ functon trong c, nhưng kỹ thuật của DFA là cách để sử dụng.

Bạn cũng có thể có một mảng

Command[] commands =
{
  new CommandA(), new CommandB(), new CommandC(), ...
}

Sau đó, bạn có thể thực hiện một lệnh theo chỉ mục

commands[7].exec();

Đạo văn từ DFA's, nhưng có một lớp cơ sở trừu tượng thay vì một giao diện. Chú ý đến cmdKey sẽ được sử dụng sau này. Theo kinh nghiệm, tôi nhận ra rằng thường một lệnh thiết bị cũng có lệnh con.

abstract public class Command()
{
  abstract public byte exec(String subCmd);
  public String cmdKey;
  public String subCmd;
}

Do đó, xây dựng các lệnh của bạn,

public class CommandA
extends Command
{
  public CommandA(String subCmd)
  {
    this.cmdKey = "A";
    this.subCmd = subCmd;
  }

  public byte exec()
  {
    sendWhatever(...);
    byte status = receiveWhatever(...);
    return status;
  }
}

Sau đó, bạn có thể mở rộng HashMap hoặc HashTable chung bằng cách cung cấp chức năng hút cặp khóa-giá trị:

public class CommandHash<String, Command>
extends HashMap<String, Command>
(
  public CommandHash<String, Command>(Command[] commands)
  {
    this.commandSucker(Command[] commands);
  }
  public commandSucker(Command[] commands)
  {
    for(Command cmd : commands)
    {
      this.put(cmd.cmdKey, cmd);
    }
  }
}

Sau đó, xây dựng cửa hàng lệnh của bạn:

CommandHash commands =
  new CommandHash(
  {
    new CommandA("asdf"),
    new CommandA("qwerty"),
    new CommandB(null),
    new CommandC("hello dolly"),
    ...
  });

Bây giờ bạn có thể gửi các điều khiển một cách khách quan

commands.get("A").exec();
commands.get(condition).exec();

+1 để đề cập đến các đại biểu trong trường hợp bất kỳ người nào .NET nhìn thấy câu hỏi này và phát điên với giao diện một phương thức. Nhưng chúng thực sự không thể so sánh với con trỏ hàm. Chúng gần hơn với phiên bản mẫu lệnh được hỗ trợ ngôn ngữ.
Daniel Earwicker

5

Tôi khuyên bạn nên tạo các đối tượng lệnh và đặt chúng vào một bản đồ băm bằng cách sử dụng Chuỗi làm khóa.


3

Ngay cả khi tôi tin rằng phương pháp tiếp cận mẫu lệnh hướng đến những lời khen ngợi tốt nhất và có thể duy trì trong thời gian dài, đây là một lựa chọn lót cho bạn:

org.apache.commons.beanutils.MethodUtils.invokeMethod (this, "doCommand" + value, null);


2

tôi thường cố gắng giải quyết nó theo cách:

public enum Command {

A {void exec() {
     doCommandA();
}},

B {void exec() {
    doCommandB();
}};

abstract void exec();
 }

điều này có nhiều lợi thế:

1) không thể thêm một enum mà không thực hiện thi hành. vì vậy bạn sẽ không bỏ lỡ điểm A.

2) bạn thậm chí sẽ không phải thêm nó vào bất kỳ bản đồ lệnh nào, vì vậy không cần mã soạn sẵn để xây dựng bản đồ. chỉ là phương thức trừu tượng và các triển khai của nó. (được cho là cũng là bản ghi sẵn, nhưng nó sẽ không ngắn hơn nữa ..)

3) bạn sẽ tiết kiệm được bất kỳ chu kỳ cpu lãng phí nào bằng cách xem qua một danh sách dài các if hoặc tính toán các mã băm và thực hiện tra cứu.

chỉnh sửa: nếu bạn không có enums mà có chuỗi làm nguồn, chỉ cần sử dụng Command.valueOf(mystr).exec()để gọi phương thức thực thi. lưu ý rằng bạn phải sử dụng công cụ sửa đổi công khai khi bạn muốn gọi nó từ một gói khác.


2

Có lẽ tốt nhất bạn nên sử dụng Bản đồ các lệnh.

Nhưng liệu bạn có một tập hợp những thứ này để xử lý bạn cuối cùng với vô số Bản đồ gõ cửa. Sau đó, nó là giá trị xem làm điều đó với Enums.

Bạn có thể làm điều đó với Enum mà không cần sử dụng công tắc (có thể bạn không cần getters trong ví dụ), nếu bạn thêm một phương thức vào Enum để phân giải cho "value". Sau đó, bạn chỉ có thể làm:

Cập nhật: thêm bản đồ tĩnh để tránh lặp lại trên mỗi cuộc gọi. Không biết xấu hổ bị chèn ép từ câu trả lời này .

Commands.getCommand(value).exec();

public interface Command {
    void exec();
}

public enum Commands {
    A("foo", new Command(){public void exec(){
        System.out.println(A.getValue());
    }}),
    B("bar", new Command(){public void exec(){
        System.out.println(B.getValue());
    }}),
    C("barry", new Command(){public void exec(){
        System.out.println(C.getValue());
    }});

    private String value;
    private Command command;
    private static Map<String, Commands> commandsMap;

    static {
        commandsMap = new HashMap<String, Commands>();
        for (Commands c : Commands.values()) {
            commandsMap.put(c.getValue(), c);    
        }
    }

    Commands(String value, Command command) {
        this.value= value;
        this.command = command;
    }

    public String getValue() {
        return value;
    }

    public Command getCommand() {
        return command;
    }

    public static Command getCommand(String value) {
        if(!commandsMap.containsKey(value)) {
            throw new RuntimeException("value not found:" + value);
        }
        return commandsMap.get(value).getCommand();
    }
}

2

Theo tôi, câu trả lời được cung cấp bởi @dfa là giải pháp tốt nhất.

Tôi chỉ cung cấp một số đoạn mã trong trường hợp bạn đang sử dụng Java 8 và muốn sử dụng Lambdas!

Lệnh không có tham số:

Map<String, Command> commands = new HashMap<String, Command>();
commands.put("A", () -> System.out.println("COMMAND A"));
commands.put("B", () -> System.out.println("COMMAND B"));
commands.put("C", () -> System.out.println("COMMAND C"));
commands.get(value).exec();

(bạn có thể sử dụng Runnable thay vì Command, nhưng tôi không cho là đúng về mặt ngữ nghĩa):

Lệnh với một tham số:

Trong trường hợp bạn mong đợi một tham số, bạn có thể sử dụng java.util.function.Consumer:

Map<String, Consumer<Object>> commands = new HashMap<String, Consumer<Object>>();
commands.put("A", myObj::doSomethingA);
commands.put("B", myObj::doSomethingB);
commands.put("C", myObj::doSomethingC);
commands.get(value).accept(param);

Trong ví dụ trên, doSomethingXlà một phương thức có trong myObjlớp của nó, lấy bất kỳ Đối tượng nào (có tên paramtrong ví dụ này) làm đối số.


1

nếu bạn có nhiều câu lệnh 'if' được ghép sẵn, thì đây là một mẫu để sử dụng công cụ quy tắc . Hãy xem, ví dụ như JBOSS Drools .



0

nếu có thể có một mảng các thủ tục (bạn gọi là lệnh) hữu ích ..

nhưng bạn có thể viết một chương trình để viết mã của bạn. Tất cả đều rất có hệ thống if (value = 'A') commandA (); khác nếu (......................... vv


0

Tôi không chắc liệu bạn có bất kỳ sự chồng chéo nào giữa hành vi của các lệnh khác nhau hay không, nhưng bạn cũng có thể muốn xem xét mẫu Chuỗi trách nhiệm có thể cung cấp tính linh hoạt hơn bằng cách cho phép nhiều lệnh xử lý một số giá trị đầu vào.


0

Mẫu lệnh là cách để thực hiện. Đây là một ví dụ sử dụng java 8:

1. Xác định giao diện:

public interface ExtensionHandler {
  boolean isMatched(String fileName);
  String handle(String fileName);
}

2. Triển khai giao diện với từng phần mở rộng:

public class PdfHandler implements ExtensionHandler {
  @Override
  public boolean isMatched(String fileName) {
    return fileName.endsWith(".pdf");
  }

  @Override
  public String handle(String fileName) {
    return "application/pdf";
  }
}

public class TxtHandler implements ExtensionHandler {
  @Override public boolean isMatched(String fileName) {
    return fileName.endsWith(".txt");
  }

  @Override public String handle(String fileName) {
    return "txt/plain";
  }
}

và như thế .....

3. Xác định Khách hàng:

public class MimeTypeGetter {
  private List<ExtensionHandler> extensionHandlers;
  private ExtensionHandler plainTextHandler;

  public MimeTypeGetter() {
    extensionHandlers = new ArrayList<>();

    extensionHandlers.add(new PdfHandler());
    extensionHandlers.add(new DocHandler());
    extensionHandlers.add(new XlsHandler());

    // and so on

    plainTextHandler = new PlainTextHandler();
    extensionHandlers.add(plainTextHandler);
  }

  public String getMimeType(String fileExtension) {
    return extensionHandlers.stream()
      .filter(handler -> handler.isMatched(fileExtension))
      .findFirst()
      .orElse(plainTextHandler)
      .handle(fileExtension);
  }
}

4. Và đây là kết quả mẫu:

  public static void main(String[] args) {
    MimeTypeGetter mimeTypeGetter = new MimeTypeGetter();

    System.out.println(mimeTypeGetter.getMimeType("test.pdf")); // application/pdf
    System.out.println(mimeTypeGetter.getMimeType("hello.txt")); // txt/plain
    System.out.println(mimeTypeGetter.getMimeType("my presentation.ppt")); // "application/vnd.ms-powerpoint"
  }

-1

Nếu nó thực hiện nhiều thứ, thì sẽ có rất nhiều mã, bạn không thể thực sự thoát khỏi điều đó. Chỉ cần giúp bạn dễ theo dõi, đặt tên cho các biến rất có ý nghĩa, nhận xét cũng có thể giúp ích ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.