지난 글에 이어서 분기 처리 코드를 줄이는 방법 중 boolean 조건을 가질 때 처리 방법에 대해 알아보자. 예시 코드는 우테코 프리코스 4 주차 미션에서 가져왔다.
불리언도 문자열과 마찬가지로 object mapping을 사용하여 분기 처리를 할 수 있다. 다만, 자바스크립트에서는 key를 문자열로만 저장하므로 아래와 같이 dynamic key를 사용하여야 boolean key를 적용할 수 있다.
const MAP = {
[true]: // 해당 코드,
[false]: // 해당 코드,
};
하지만 비교해야 할 boolean 값들이 여러 개라면 어떻게 처리해야 할까? 예를 들어 2 개의 불리언 상태를 사용한다면 우리는 4 개의 경우의 수를 생각할 수 있다. 그러나 boolean 조건을 연산한 결과는 결국 true
false
두 개의 결과만을 가지므로 우리는 4 개가 아닌 2 개의 dynamic key만을 갖게 되는 문제가 발생한다.
여러 boolean 값을 하나의 string으로 처리하여 이전 글과 동일하게 object mapping을 사용해보자!
우리는 일반적으로 두 개의 boolean 상태 isFinish
isMove
를 연산하여 아래와 같은 분기 처리를 한다. Boolean 상태가 많아질수록 코드가 점점 더 길어질 것이며, 조건문에 각 상태의 true
false
여부가 뒤섞여 가독성도 나빠질 것이다.
checkContinueMove() {
const { isFinish, isMove } = this.getBooleans();
if (!isFinish && isMove) {
this.getPlayerMove();
} else if (!isMove) {
this.getGameCommand();
} else if (isFinish && isMove) {
this.quit();
}
}
이 문제를 해결하기 위해서 다음의 방법을 사용할 수 있다.
- 여러 boolean 상태 ➡️ 하나의 숫자로 나타내기
- 해당 숫자를 array의 index로 사용하여 mapping
참과 거짓으로 나타나는 boolean 값은 컴퓨터에서는 1과 0의 숫자로 인식된다. 아래 그림의 좌측과 같이 N 개의 boolean 상태들을 나열하면 1과 0의 집합이 되고, 이들을 이진수의 각 N -1 자리수에 대입하면 하나의 이진수로 대체할 수 있다.
앞서 다룬 isFinish
isMove
2 개의 boolean 상태를 다시 예시로 들어보자. 각 boolean 상태는 0 과 1 두 개의 값을 가지며 경우의 수는 4 가지이다.
각 boolean 값을 하나의 이진수에 저장하기 위해서는 bitwise left shift(<<) 연산자를 사용한다. n 번 shift(<<)하면 이진수의 n 자리에 저장된다.
const binary1 = true; // 이진수: 1, 십진수:1
const binary2 = true << 1; // 이진수: 10, 십진수:2
const binary3 = true << 2; // 이진수: 100, 십진수:4
const binary4 = true << 2 | true ; // 이진수: 101, 십진수:5
이를 활용하여 우리는 두 개의 boolean 값을 아래와 같이 하나의 playerState
로 나타낼 수 있다. 이 값은 4 가지 경우의 수를 가지며 십진수로 0, 1, 2, 3로 나타난다.
const playerState = (isFinish << 1) | isMove;
이를 활용하여 우리는 #1.1의 메인로직을 아래와 같이 간결하게 나타낼 수 있다.
// Main logic
checkContinueMove() {
const { isFinish, isMove } = this.getBooleans();
const playerState = createPlayerState(isFinish, isMove);
PLAYER_STATE_FN[playerState](this);
}
비트 연산을 활용하여 두 개의 boolean 값을 하나의 숫자로 나타내고, PLAYER_STATE_ARRAY
에서 해당 PLAYER_STATE
를 인덱스로 하는 값을 찾는다. 그리고 이전 글과 같이 object mapping으로 함수를 key로 가져와 활용하면 분기 처리 로직을 단순화할 수 있다.
// Bitwise shift
const createPlayerState = (isFinish, isMove) => {
const state = (isFinish << 1) | isMove;
return PLAYER_STATE_ARRAY[state];
};
// Array mapping
const PLAYER_STATE_ARRAY = [
"retry",
"continue",
"retry",
"success",
];
// Object mapping
const PLAYER_STATE_FN = {
retry(gamePresenter) {
gamePresenter.getGameCommand();
},
continue(gamePresenter) {
gamePresenter.getPlayerMove();
},
success(gamePresenter) {
gamePresenter.quit();
},
};
예를들어 isFinish=true
isMove=false
인 경우,
playerState
는 2로 반환되고 배열의 세 번째 값인 retry
가 맵핑되어
gamePresenter.getGameCommand()
함수가 실행된다.
추가로 and 연산자를 활용하면 하나로 합친 이진수 값에서 해당 자리 수의 boolean 값을 가져올 수도 있다.
const isFinish = playerState & ( 1 << 1 );
비트 연산의 개념만 알고 직접 적용해보지는 못했었는데 분기 처리에 활용해보니 왜 기초 CS 지식이 중요한지 알게되었다. 여전히 코드가 얽혀있고 복잡하지만 한 번에 독파한다는 생각보다는 이렇게 하나씩 개선해나가는 것도 좋은 방법이라 생각한다.
#1.2 1+ 를 보면서 생긴 궁금증입니다. const a = 1 이고 const b = 3 일때 a & b = 1 의 값을 얻을 수 있는데 위 설명대로라면 알고싶은 boolean 값자리가 1 일때 0 이 나와야하는 것 아닌가요?