2017-12-14 11 views
0

string에서 콘솔 명령을 처리 할 콘솔을 만들려고합니다.콘솔 명령을 처리하기위한 최상의 Java 디자인 패턴

문이 같은 각 명령에 대해 확인하는 경우, 내가 사용하고 순간

:

if (command.contains("new train")) { 
    command = command.replace("new train ", ""); 
    Train t = new Train(); 
    t.setCode(command); 
    ServiceProvider.getTrainService().saveOrUpdate(t); 
    responeHandler("train " + command + " created"); 
} 

그러나 이것은 제 생각에 가장 좋은 방법이 아닙니다.

이미 이런 문제에 대한 좋은 디자인 패턴이 있는지 궁금합니다.

나는 builderfactory 패턴을 보았지만 실제로 올바른 선택인지는 결정할 수 없습니다.

답변

1

CommandFactory 패턴을 어쩌면?

interface Command { 
    void execute(); 
} 

interface CommandFactory { 
    boolean canCreate(String input); 
    Command fromInput(String input); // or return Optional so it can be a FunctionalInterface 
} 

class TrainCommand implements Command { 
    String train; 
    public TrainCommand(String t) { train = t; } 
    public void execute() { 
     ServiceProvider.getTrainService().saveOrUpdate(t); 
    } 
} 

class TrainCommandFactory { 
    public boolean canCreate(String t) { 
     return t.contains("new train "); 
    } 
    public Command fromString(String c) { 
     return new TrainCommand(c.replace("new train ", "")); 
    } 
} 

그리고 알려진 모든 명령 공장을 반복 할 SingletonComposite CommandFactory : 당신이 봄 세계에있는 경우

class CommandFactories implements CommandFactory { 
    private static final CommandFactories INSTANCE; 
    private List<CommandFactory> delegates = Arrays.asList(
     new TrainCommandFactory() 
     // others 
    }; 
    public boolean canCreate(String t) { 
     return delegates.stream() 
      .filter(cf -> cf.canCreate(t)) 
      .findAny().isPresent(); 
    } 
    public Command fromString(String c) { 
     return delegates.stream() 
      .filter(cf -> cf.canCreate(t)) 
      .findAny() 
      .map(CommandFactory::fromString); 
    } 
} 
+0

멋져 보이지만 실제로 호출 될 때마다 내 코드가 TrainCommandFactory에서 새로운 명령 인스턴스를 생성해야합니까? 또한'Singleton'' Composite'로 정확히 무엇을 의미합니까? 합성물은 내가 맞으면 객체의 나무와 같습니다. 그리고 싱글 톤은 오직 하나의 인스턴스 만 가질 수 있습니다. 그 2 개를 어떻게 연결시켜 주시겠습니까? – klokklok

+0

Composite Singleton Factory에 대한 내 대답을 편집했습니다. 매번 명령 인스턴스를 생성하고 싶지 않으면'TrainCommand' 싱글 톤을 만들고'fromString'이 호출 될 때마다 팩토리를 반환하도록 할 수 있습니다. 하지만 String 인자를 받아들이려면'execute'를 바꿔야합니다. – daniu

+0

고마워요. 저는 당신과 Martin Frank의 답변을 조합하여 사용했습니다! – klokklok

1

Map<String, Consumer<String>>은 명령을 작업에 연결하는 작업을 수행 할 수 있습니다.
GOF 공장 및 DP 명령이 아닙니다.
그러나 이것은 공장 및 명령 패턴의 공정하고 간단한 구현입니다.
그래서 고려해야합니다.

Map<String, Consumer<String>> actionsByCommand = new HashMap<>(); 

actionsByCommand.put("new train", command->{ 
    command = command.replace("new train ", ""); 
     Train t = new Train(); 
     t.setCode(command); 
     ServiceProvider.getTrainService().saveOrUpdate(t); 
     responeHandler("train " + command + " created"); 
}); 

actionsByCommand.put("delete train", command->{ 
    command = command.replace("delete train ", ""); 
    ... 
}); 

// and so for... 

당신은 또한 당신이지도에 넣어 필요가 없습니다 무효 명령에 대한 특별한 조치 만들 수 있습니다,

Consumer<String> invalidCommandAction = (command-> System.out.println("Invalid command. Here are the accepted commands...")); 

이 테스트 용이성과 행동 클래스의 유지 보수성을 향상시키기 위해 당신이 할 수 그들을 다른 클래스로 옮기십시오.

public class NewTrainAction implements Consumer<String>{ 

    public void accept(String command){ 
     command = command.replace("new train ", ""); 
     Train t = new Train(); 
     t.setCode(command); 
     ServiceProvider.getTrainService().saveOrUpdate(t); 
      responeHandler("train " + command + " created"); 
    } 

} 

과 같은 방법으로 정의 된 다른 작업

: NewTrainAction

Map<String, Consumer<String>> actionsByCommand = new HashMap<>(); 

actionsByCommand.put("new train", new NewTrainCommand()); 
actionsByCommand.put("delete train", new DeleteTrainCommand()); 

는 다음과 같이 정의했다.

그럼 당신은이 방법을 사용할 수 있습니다

Scanner scanner = new Scanner(System.in); 
while (scanner.hasNextLine()) { 
    String command = scanner.nextLine(); 
    Consumer<String> action = actionsByCommand.getOrDefault(command, invalidCommandAction); 
    action.accept(command); 
} 
+0

이 답변으로 참조 할 수있는 패턴이 있습니까? 커다란 기능을 가진 동일한 클래스를 사용하는 대신 클래스에 내 명령을 분할하려고합니다. – klokklok

+0

요구 사항 및 질문에 대한 답변을 업데이트했습니다. – davidxxx

0

당신이

이 당신은 고려할 수

을 구현하는 데 사용할 수있는

org.springframework.boot.CommandLineRunner 

각 명령을 실행할 수 있음 CommandLineRunne 인스턴스에서.

사용

org.springframework.core.env.SimpleCommandLinePropertySource 

는 명령 줄 구문 분석

1

당신은 배열에있는 당신의 명령을 저장할 수 있고 사용자가 명령을 입력 할 때, 당신은 주어진 인덱스를 갖는 항목을 찾을 수 있습니다. 색인은 의미가 있으며 스위치 케이스에서 사용할 수 있으며 항목을 찾을 수없는 경우 의미있는 응답을 줄 수 있습니다. 내가 정의 명령을 사용하여 생각

protected String[] supportedCommands = {"first", "second", "third"}; 
public static int findCommand(String command) { 
    for (var i = 0; i < supportedCommands.length; i++) { 
     if (command.equals(supportedCommands[i])) return i; 
    } 
    return -1; //Not found 
} 

public static void handleCommand(String command) { 
    int c = findCommand(command.toLowerCase()); 
    switch (c) { 
     case 1: {/*...*/} break; 
     default: {/*Handle Undefined command*/} 
    } 
} 
+0

저는 커맨드를 좋은 패턴으로 클래스로 분리하려고합니다. 당신의 대답을 보면 나는 여전히 같은 수업에 모든 코드를 쓰고있을 것입니다. 이렇게하면 스위치 케이스와 어레이가 시간이 지남에 따라 커지고 커집니다. (새 명령을 추가 할 때) – klokklok

+0

@klokklok는 엔진과 동일한 클래스의 명령을 유지하지 않습니다. 필요에 따라 분리 할 수 ​​있습니다. –

+0

당신은 맞지 않습니다. 그러나 제 문제는 이러한 클래스들을 올바른 방법으로 분리하는 방법을 알지 못했다는 것입니다. 나는 모든 것을 서로 밑에 두는 것을 피하고 싶었다. – klokklok

1

:

또한, 배열의 모든 하위 문자 키를 가진하고 검색하기 전에 아래에 명령 키를 바꾸는 경우를 구분 방식으로이 작업을 수행 할 수 적절한 길. 명령에 대한 중요한 쟁점은 commandString으로 식별되어 (matches) 실행되어야합니다 (execute). 사용자 정의 Command을 작성하면이를 목록에 등록하고 실행할 수 있습니다. 당신이 모든 명령 목록 (또는 다른 컬렉션)에 이러한 명령 저장소를 사용하여 검사 할 경우 명령은 입력과 일치하는 경우

interface Command{ 
    boolean matches(String commandString); 
    boolean execute(String commandString); 
} 

구현 예는

CreateTrainCommand implements Command{ 

    private final CMDSTRING = "new train"; 

    @Override 
    public boolean matches(CommandString cmdStr){ 
     if(cmdStr != null && cmdStr.toLowerCase.startsWith(CMDSTRING)){ 
      return true; 
     } 
     return false; 
    } 

    @Override 
    public boolean matches(CommandString cmdStr){ 
     if(cmdStr != null){ 
      String train = cmdString.toLowerCase.replace(CMDSTRING, "").trim(); 

      //here comes your command execution 
      Train t = new Train(); 
      ... 
     } 
     return true;//execution succesful 
    } 
} 

을 것

List<Command> cmds = ... 
cmds.add(new CreateTrainCommand()); //add all commands that were implemented 
//or only some commands if the user has restricted access 

여기에 명령을 적용하는 방법은 다음과 같습니다.

String commandString = ... //from Scanner or where else 
for(Command cmd: cmds){ //use streams if you're java 8 
    if (cmd.matches(commandString)){ 
     boolean wasSuccesful = cmd.execute(commandString); 
     break; 
    } 
}