들어가며
체스 미션을 진행하면서 상태 패턴을 사용하였다. 상태 패턴을 적용한 이유는 체스 게임이 시작, 종료, 진행 상태로 나누어진다고 생각하였다. 이에 대해 정리해보려고 한다.
상태 패턴
내부 상태를 바꿈으로써 객체가 행동을 바꿀 수 있게 해준다. 별도의 클래스로 캡슐화 한 후 다음 상태를 나타내는 객체에게 행동하도록 요구하여 상태를 변경할 수 있따.
체스 미션에는 시작, 진행, 종료 상태가 있다. 이 상태에 따라 코드를 구현하고자 한다면
final static int START = 0;
final static int RUNNING = 1;
final static int END = 2;
int state = END;
public void start(){
if (state == start) {
state = RUNNING;
}
if (state == running) {
state = END;
}
if (state == end) {
state = START;
}
}
// 설명을 위해 대충 만든 예시 ..
이런 식으로 각 행동을 구현할 때 조건문을 써서 상태별로 어떤 작업을 처리해야 되는지 결정해야 한다.
행동 별로 메소드를 만들어 계속해서 구현해야 한다.
하지만 이럴 경우 if문이 계속 생기고, 코드를 확장하는 것이 어렵다.
개선안
기존 코드를 활용하는 대신 상태 객체들을 별도의 코드에 넣는다. 그래서 어떤 행동이 일어나면 그 객체에서 작업을 처리하도록 하는 것이다.
체스 게임과 관련된 모든 행동 메소드가 들어있는 State인터페이스를 만든다. →
기계의 모든 상태에 대해 상태 클래스를 구현한다. (이렇게 함으로써 특정 상태가 특정 작업을 처리하게 한다) →
상태클래스가 모든 작업을 처리한다.
상태 종류
처음에는 START → RUNNING → END 라고 생각했으나 후에 이는
READY → RUNNING → END로 변경하였고 이 경우 Running이 흰팀, 검은팀 상태를 모두 맡아야 하는 책임이 큰 것 같아서
READY → WHITE → BLACK → END 상태로 변경하였다.
State 인터페이스
public interface State {
boolean isEnd();
State run(Command command, Board board);
}
특정 상태가 종료 상태인지, 특정 상태에서 게임이 실행될 때 를 나누어 메소드를 작성하였다.
Ready 상태
public class Ready implements State {
@Override
public boolean isEnd() {
return false;
}
@Override
public State run(Command command, Board board) {
if (command.isStart()) {
return new White();
}
if (command.isMove()) {
throw new UnsupportedOperationException("move를 입력할 수 없습니다.");
}
if (command.isStatus()) {
throw new UnsupportedOperationException("status를 입력할 수 없습니다.");
}
if (command.isEnd()) {
return new End();
}
return new Ready();
}
}
Ready 상태 일 때는
End 가 아님을 검증하고, 이 경우 게임을 실행했을 때
Start → White 상태로
Move → 입력 불가
Status → 입력 불가
End → End 상태로
그 외엔 다시 Ready상태로 진행할 수 있도록 하였다.
White & Black 상태
public class White implements State {
@Override
public boolean isEnd() {
return false;
}
@Override
public State run(Command command, Board board) {
if (command.isEnd()) {
return new End();
}
if (command.isMove()) {
board.move(command.getSourcePoint(), command.getTargetPoint(), Team.WHITE);
return selectNextState(board);
}
return this;
}
private State selectNextState(Board board) {
if (!board.isExistKing(Team.BLACK)) {
return new End();
}
return new Black();
}
}
특정 색상 상태일 경우에는
End가 아님을 검증하고 실행시에는
End → End 상태로
Move → 왕이 잡혔을 경우 End 상태로
그 외에는 Black 상태로
그 외엔 다시 그외의 색 상태로 갈 수 있도록 설정하였다.
End 상태
public class End implements State {
@Override
public boolean isEnd() {
return true;
}
@Override
public State run(Command command, Board board) {
if (!command.isEnd()) {
throw new UnsupportedOperationException("start만 입력 가능 합니다.");
}
return this;
}
}
End 상태에서는
End인지 검증하고, 이상태에서 실행할 경우
모든 경우에 관해 Start만 입력 가능하도록 설정해두었다.
Game
이 상태들을 갈아끼우거나 수정하는 역할은 Game에서 진행한다.
public void setState(Command command) {
state = state.run(command, board);
if (!state.isEnd()) {
turn = state;
}
}
이를 통해 game에서 state의 다형성을 이용하여 상태를 갈아끼운다.
상태 패턴과 전략 패턴의 차이점 ?
용도가 다르다.
전략 패턴은 어떤 전략 객체를 사용할지 지정함으로써 선택해서 사용한다. 상속을 사용할 때보다 유연하게 사용하기 위해서이다.
상태 패턴은 상태 행동이 캡슐화 되어 내부 상태에 따라 현재 상태를 나타내는 객체도 계속해서 변경된다.
나가며
상태 패턴을 사용하니 훨씬 편하고 코드를 읽기가 좋았다.
무조건적으로 상태패턴을 사용하는 것은 지양해야겠지만 개선을 위한 디자인 패턴을 사용하는 것은 좋은 것 같다.
'코코코딩공부 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] 전략 패턴 (0) | 2023.02.27 |
---|---|
[개발 패턴] TDD (0) | 2023.02.14 |
[디자인 패턴] MVC 패턴 (0) | 2023.02.12 |