체스 미션을 진행하면서 커맨드 패턴에 대해 듣게 되었다.
디자인 패턴을 사용하는데만 집중하는 것은 좋지 않다는 말이 있지만,
분기문에 대한 불편함, 찝찝함을 해소하고 싶었다.
알면서 안쓰는 것과 모르면서 안쓰는 것과는 큰 차이이기 때문에 일단 적용해 보고자 했다.
어떻게 쓰는 것인진 모르겠으나, 커맨드에 따라 책임을 분리한다는 생각만 가지고 내 나름대로 구현을 해 보았다.
커맨드 패턴과 상태 패턴의 결합
Command
인터페이스에서 커맨드를 실행하는 execute()
를 가지고 있다.
public interface Command {
State execute(Optional<ChessGame> chessGame, List<String> input);
String getCommand();
}
사용할 커맨드들을 객체로 만들어서 Command
를 구현하도록 한다. 내가 사용한 커맨드는 Start
, Move
, Status
, End
이다.
커맨드를 관리하는 Commands
를 만들어줘서 사용 가능한 커맨드를 관리한다.
public class Commands {
private final Map<String, Command> commands;
public Commands(List<Command> commands) {
this.commands = commands.stream()
.collect(Collectors.toMap(Command::getCommand, Function.identity()));
}
public Command findCommand(String input) {
return commands.getOrDefault(input, new IllegalCommand());
}
}
다음과 같이 커맨드를 사용하는 곳에서 어떤 커맨드를 사용할 수 있는지 정해줄 수 있다.
Commands commands = new Commands(List.of(new MoveCommand(), new EndCommand(), new StatusCommand()));
사용하지 않는 커맨드라면 IllegalCommand
를 exceute하게 되고, 여기에는 UnsupportedException을 발생시키도록 만들었다.
다음은 이를 구현하는 MoveCommand
클래스이다. 커맨드를 실행하면 Move
객체를 생성한다.
@Override
public State execute(Optional<ChessGame> chessGame, List<String> input) {
return new Move(chessGame.orElseThrow(IllegalArgumentException::new),
input.get(1), input.get(2));
}
커맨드에 따라서 상태에 맞는 객체를 생성하도록 했다.
느낀점
사실 상태를 체스 게임에 적용하는 것이 보통인데, 나는 만들다 보니 컨트롤러를 상태로 제어하게 되었다.
컨트롤러를 상태로 제어하기 때문에 파라미터로 InputView
와 OutputView
를 넣어주는 것이 조금 부자연스러운 느낌이 있다.
또한, Ready
상태에서는 ChessGame
를 초기화 하지 않은 상태이기 때문에 Optional<ChessGame>
으로 래핑해주었다.
파라미터에 Optional을 사용하는 것이 안좋아 보이지만 당장은 어쩔 수 없이 사용하고 나중에 리팩토링 해야겠다.
분기문이 확연하게 줄어든 것을 느낄 수 있었다.
Status
는 나중에 추가된 상태인데, 코드의 확장성이 나아진 것을 바로 느낄 수 있었다.
특히, 상태에 따라서 사용 가능한 커맨드를 제어해주는 것이 괜찮다고 생각했다.
'회고 > 우아한테크코스' 카테고리의 다른 글
[레벨 1] 레벨 로그 (0) | 2023.03.28 |
---|---|
2023.03.27 일일 회고 (0) | 2023.03.28 |
2023.03.21 일일 회고 (0) | 2023.03.22 |
2023.03.20 일일 회고 (0) | 2023.03.21 |
[레벨 1] 사다리 미션 회고 (0) | 2023.03.20 |