본문 바로가기

카테고리 없음

[오브젝트] 명령-쿼리 분리 원칙

프로시져와 함수

어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈을 루틴이라고 한다. 이 루틴은 프로시져와 함수로 구분할 수 있다.

프로시져와 함수는 부수효과와 반환 값의 유무라는 측면에서 명확하게 구분된다.

프로시져는 정해진 절차를 따라 내부의 상태를 변경하는 루틴의 한 종류이고 함수는 어떤 절차에 따라 필요한 값을 계산에서 반환하는 루틴의 한 종류이다.

  • 프로시저는 부수효과를 발생시킬 수 있지만 값을 반환할 수 없다.
  • 함수는 값을 반환할 수 있지만 부수효과를 발생시킬 수 없다.

명령(Command)와 쿼리(Query)는 객체의 인터페이스 측면에서 프로시져와 함수를 부르는 또 다른 이름이다. 객체의 상태를 수정하는 오퍼레이션을 명령이라고 부르고 객체와 관련된 정보를 반환하는 오퍼레이션을 쿼리라고 부른다.

즉 개념적으로 명령은 프로시져와 동일하고 쿼리는 함수와 동일하다.

 

명령 - 쿼리 분리 원칙

명령 - 쿼리 분칙의 요지는 오퍼레이션은 부수효과를 발생시키는 명령이거나 부수효과를 발생시키지 않는 쿼리 중 하나이다. 어떤 오퍼레이션도 명령인 동시에 쿼리여서는 안된다. 따라서 명령과 쿼리를 분리하기 위해서는 다음의 두 가지 규칙을 준수해야 한다.

  • 객체의 상태를 변경하는 명령은 반환 값을 가질 수 없다.
  • 객체의 정보를 반환하는 쿼리는 상태를 변경할 수 없다.


이렇게 명령과 쿼리를 엄격하게 분류하면 객체의 부수효과를 제어하기가 수월해진다. 만약 쿼리에 상태를 변경하는 값이 들어있다면 어떤 버그가 생겼을 때 디버깅하기가 까다로워진다.

 

수학에서의 함수는 같은 input이 들어간다고 했을 때 무조건 같은 output을 보장하지만 컴퓨터의 함수에서는 그렇지 않다. 따라서 컴퓨터의 세계와 수학의 세계를 나누는 가장 큰 특징을 부수효과라고 할 수 있다.

 

하지만 명령과 쿼리를 분리함으로서 참조 투명성의 장점을 제한적으로 누릴 수 있게 된다.

참조 투명성이란 어떤 표현식 e가 있을 때 모든 e를 e의 값으로 바꾸더라도 결과가 달라지지 않는 특성을 의미한다.

 

수학의 함수는 이러한 참조 투명성을 잘 만족하는데, 어떤 함수의 input이 1일 때 3이라는 Output이 무조건 보장되는데 이처럼 어떤 값이 변하지 않는 성질을 불변성이라고 한다. 어떤 값이 불변한다는 말은 부수효과가 발생하지 않는다는 것을 의미하게 된다.

 

부수효과가 없는 불변의 세상에서는 모든 로직이 참조 투명성을 만족시킨다.

그래서 여러 함수의 순서와 위치를 조정하더라도 각 식의 결과를 그대로 유지시키는 결과를 볼 수 있다.

 

객체지향 패러다임은 객체의 상태 변경이라는 부수효과를 기반으로 하기 때문에 참조 투명성은 예외에 가까운데, 이 명령-쿼리 분리 원칙을 준수한다면 이 균열을 조금이나마 줄이고 참조 투명성의 혜택을 누릴 수 있게 된다.

 

아래 간단한 표의 개념을 잘 기억해두면 된다.

특징 명령, 프로시져 쿼리, 함수
부수효과 발생 O X
값 반환 X O

하지만 당연히 실제로 코드를 작성하다보면 예외가 생기기 마련이다. 이러한 예외는 유연하게 받아 들이고 주석을 잘 작성해두면 된다. 예를 들어 stack에서 pop은 대표적인 명령-쿼리 분리 원칙을 어긴 예이다. pop은 stack에서 가장 끝에 있는 원소를 빼는 것과 동시에 그 값을 반환하기 때문이다.