[java] 자바의 if 문의 긴 목록

이 질문에 대한 답을 찾을 수 없어서 죄송합니다. 다른 사람이 이전에 질문을 제기 한 것 같습니다.

내 문제는 임베디드 장치를 실행하기 위해 일부 시스템 라이브러리를 작성하고 있다는 것입니다. 라디오 방송을 통해 이러한 장치로 보낼 수있는 명령이 있습니다. 이것은 텍스트로만 수행 할 수 있습니다. 시스템 라이브러리 내부에는 다음과 같은 명령을 처리하는 스레드가 있습니다.

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

문제는 그것에 대한 많은 명령이 통제 불능으로 빠르게 나선다는 것입니다. 밖을 내다 보는 것은 끔찍하고 디버그하는 것은 고통스럽고 몇 달 안에 이해하기가 벅차 오릅니다.



답변

사용하여 명령 패턴 :

public interface Command {
     void exec();
}

public class CommandA() implements Command {

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

// etc etc

그런 다음 Map<String,Command>개체 를 빌드하고 Command인스턴스로 채 웁니다 .

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

그런 다음 if / else if 체인을 다음으로 바꿀 수 있습니다 .

commandMap.get(value).exec();

편집하다

당신은 또한 다음과 같은 특별한 명령을 추가 할 수 있습니다 UnknownCommand또는 NullCommand,하지만 당신은 필요 CommandMap위해 핸들이 코너의 경우 클라이언트의 검사를 최소화 할 수있다.


답변

내 제안은 enum과 Command 객체의 가벼운 조합입니다. 이것은 Effective Java의 항목 30에서 Joshua Bloch가 권장하는 관용구입니다.

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();
}

물론 매개 변수를 doCommand에 전달하거나 반환 유형을 가질 수 있습니다.

이 솔루션은 doCommand의 구현이 enum 유형에 실제로 “적합”하지 않은 경우에는 적합하지 않을 수 있습니다. 이는 일반적으로 트레이드 오프를해야 할 때처럼 약간 모호합니다.


답변

명령 열거 형 :

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;
}

몇 개 이상의 명령이있는 경우 다른 곳에서 대답 한대로 Command 패턴을 사용하는 것이 좋습니다 (HashMap을 사용하는 대신 enum을 유지하고 enum 내에 구현 클래스에 대한 호출을 포함 할 수 있음). 이 질문에 대한 Andreas 또는 jens의 답변을 참조하십시오.


답변

dfa에 의해 간단하고 명확하게 입증 된대로 인터페이스를 구현하는 것은 깨끗하고 우아합니다 (그리고 “공식적으로”지원되는 방식). 이것이 인터페이스 개념의 의미입니다.

C #에서는 c에서 functon 포인터를 사용하려는 프로그래머를 위해 대리자를 사용할 수 있지만 DFA의 기술이 사용하는 방법입니다.

당신도 배열을 가질 수 있습니다

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

그런 다음 색인별로 명령을 실행할 수 있습니다.

commands[7].exec();

DFA에서 표절되지만 인터페이스 대신 추상 기본 클래스가 있습니다. 나중에 사용될 cmdKey에 주목하십시오. 경험을 통해 나는 종종 장비 관리자가 하위 명령을 가지고 있음을 알고 있습니다.

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

따라서 명령을 구성하십시오.

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

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

그런 다음 키-값 쌍 빠는 함수를 제공하여 일반 HashMap 또는 HashTable을 확장 할 수 있습니다.

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);
    }
  }
}

그런 다음 명령 저장소를 구성하십시오.

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

이제 객관적으로 컨트롤을 보낼 수 있습니다.

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


답변

글쎄, 나는 명령 객체를 만들고 String as Key를 사용하여 해시 맵에 넣는 것이 좋습니다.


답변

명령 패턴 접근 방식이 최선의 방법에 더 가깝고 장기적으로 유지 관리 할 수 ​​있다고 생각하더라도 여기에 한 가지 라이너 옵션이 있습니다.

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


답변

나는 보통 그렇게 해결하려고 노력합니다.

public enum Command {

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

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

abstract void exec();
 }

이것은 많은 장점이 있습니다 :

1) exec를 구현하지 않고 열거 형을 추가 할 수 없습니다. 그래서 당신은 A를 놓치지 않을 것입니다.

2) 명령 맵에 추가하지 않아도되므로 맵을 구축하기위한 상용구 코드가 없습니다. 추상적 인 방법과 그 구현. (논문의 여지가 있지만 상용구이기도하지만 더 짧아지지는 않을 것입니다.)

3) if의 긴 목록을 살펴 보거나 hashCode를 계산하고 조회를 수행하여 낭비되는 CPU 사이클을 저장합니다.

편집 : enum이 없지만 문자열이 소스로 있으면 Command.valueOf(mystr).exec()exec 메서드를 호출하는 데 사용 하십시오. 다른 패키지에서 호출하려는 execif에 public 한정자를 사용해야합니다.