2. 이상한 나라의 객체

2. 이상한 나라의 객체

앨리스의 상태를 결정하는 것은 행동이지만 행동의 결과를 결정하는 것은 상태다. 앨리스가 케이크를 먹거나 부채질을 하기 전에 엘리스의 키가 얼마 였느냐가 케이크를 먹거나 부채질을 한 후의 앨리스의 키를 결정한다. 따라서 앨리스가 한 행동의 결과는 앨리스의 상태에 의존적이다.

어떤 행동의 성공 여부는 이전에 어떤 행동들이 발생했는지에 영향을 받는다는 사실도 눈여겨보기 바란다. 앨리스가 문을 성공적으로 통과하기 위해서는 문을 통과할 수 있을 정도로 충분히 몸을 작게 줄여야 한다. 따라서 앨리스는 문을 통과하기 전에 먼저 키를 작게 줄이기 위해 병안의 음료나 케이크를 먹어야 한다.

행동에 의해 앨리스의 상태가 변경되더라도 앨리스가 앨리스라는 사실은 변하지 않는다. 앨리스의 키가 집을 부숴버릴 정도로 커지거나 쐐기벌레를 올려다 봐야 할 정도로 작아지더라도 앨리스는 앨리스다.

🖋️

지금까지 설명한 내용을 바탕으로 앨리스의 특징을 요약해보자

  • 앨리스는 상태를 가지며 상태는 변경 가능하다.
  • 앨리스의 상태를 변경시키는 것은 앨리스의 행동이다.
    • 행동의 결과는 상태에 의존적이며 상태를 이용해 서술할 수 있다.
    • 행동의 순서가 결과에 영향을 미친다.
  • 앨리스는 어떤 상태에 있더라도 유일하게 식별 가능하다.

객체 그리고 소프트웨어 나라

하나의 개별적인 실체로 식별 가능한 물리적인 또는 개념적인 사물은 어떤 것이라도 객체가 될 수 있다. 객체의 다양한 특성을 효과적으로 설명하기 위해서는 객체를 상태(state), 행동(behavior), 식별자(identity)를 지닌 실체로 보는 것이 가장 효과적이다.

🖋️

이 책에서는 객체를 다음과 같이 정의 한다.

객체란 식별 가능한 개체 또는 사물이다. 객체는 자동차처럼 만질 수 있는 구체적인 사물일 수도 있고, 시간처럼 추상적인 개념일 수도 있다. 객체는 구별 가능한 식별자, 특징적인 행동, 변경 가능한 상태를 가진다. 소프트웨어 안에서 객체는 구별 가능한 식별자, 특징적인 행동, 변경가능한 상태를 가진다. 소프트웨어 안에서 객체는 저장된 상태와 실행 가능한 코드를 통해 구현 된다.

상태

모든 일들의 공통점은 어떤 행동의 결과는 과거에 어떤 행동들이 일어났었는가에 의존한다는 것이다.

상태를 이용하면 과거의 모든 행동 이력을 설명하지 않고도 행동의 결과쉽게 예측하고 설명할 수 있다. 앨리스가 과거에 어떤 행동을 했었는지 모르더라도 앨리스의 키만 알면 문을 통과할 수 있는지 여부를 쉽게 판단할 수 있다. 즉, 앨리스의 키와 문의 높이라는 두 가지 상태만 알면 문을 통과하는 행동의 결과를 쉽게 예측할 수 있다.

상태를 이용하면 과거에 얽매이지 않고 현재를 기반으로 객체의 행동 방식을 이해할 수 있다. 상태는 근본적으로 세상의 복잡성을 완화하고 인지 과부화를 줄일 수 있는 중요한 개념이다.

앨리스가 문을 통과하는 소동에는 객체인 것과 아닌 것이 공존한다.

  • 앨리스와 앨리스의 키를 줄이거나 늘리는 케이크, 부채, 버섯 들은 모두 객체다. 이들은 뚜렷한 경계를 가지며 식별 가능하고 상태와 행동을 지니고 있다.
  • 앨리스의 ‘키’와 ‘위치’, 음료와 케이크의 ‘양’은 객체가 아니다. 또한 문이 열려있는지 ‘여부’는 객체가 아니다.

단순한 값들은 객체가 아니다. 단순한 값들은 그 자체로 독립적인 의미를 가지기보다는 다른 객체의 특성을 표현하는 데 사용된다. 다시 말해 객체의 상태 표현하기 위해 사용된다.

때로는 단순한 값이 아니라 객체를 사용해 다른 객체의 상태를 표현해야 할 때가 있다. 앨리스가 현재 음료를 들고 있는 상태인지 표현하고 싶다면 어떻게 할 것인가? 가장 간단하고 직관적인 방법은 앨리스의 상태 일부를 음료라는 객체를 이용해 표현하는 것이다.

이때 객체의 상태를 구성하는 모든 특징을 통틀어 객체의 프로퍼티(Property)라고 한다. 일반적으로 프로퍼티는 변경되지 않고 고정되기 때문에 ‘정적’이다. 반면 프로퍼티 값(Property Value)은 시간이 흐름에 따라 변경되기 때문에 ‘동적’이다.

앞선 그림에서는 앨리스와 음료 사이에 선이 존재 했지만, 위의 그림에서는 없다. 이는 앨리스가 음료에 관해 알고 있었지만, 음료에 관해 알지 못하는 상태로 변경됐음을 의미한다.

이처럼 객체와 객체사이의 의미 있는 연결을 링크라고 한다. 객체와 객체 사이에는 링크가 존재해야만 요청을 보내고 받을 수 있다. 즉, 객체의 링크를 통해서만 메시지를 주고 받을 수 있다. 링크는 객체가 다른 객체를 참조할 수 있다는 것을 의미하며, 이것은 한 객체가 다른 객체의 식별자를 알고 있다는 것으로 표현된다.

링크와 달리 단순한 값은 속성(attribute)이라고 한다. 앨리스의 키와 위치가 이에 해당한다. 객체의 프로퍼티는 단순한 값인 속성과 다른 객체를 가리키는 링크 두가지 조합으로 표현할 수 있다. ( 정확하게 말하면 프로퍼티는 속성과 연관관계의 두 가지 종류로 구분된다. 연관 관계는 정적인 관계를 의미하며 링크는 연관 관계의 인스턴스다.)

🖋️

이 책에서는 객체의 상태를 다음과 같이 정의하기로 한다.

상태는 특정 시점에 객체가 가지고 있는 정보의 집합으로 객체의 구조적 특징을 표현한다. 객체의 상태는 객체에 존재하는 정적인 프로퍼티와 동적인 프로퍼티 값으로 구성된다. 객체의 프로퍼티는 단순한 값과 다른 객체를 참조하는 링크로 구분할 수 있다.

객체지향의 세계에서 객체는 다른 객체의 상태에 직접적으로 접근할 수도, 상태를 변경할 수도 없다. 자율적인 객체는 스스로 자신의 상태를 책임져야 한다. 외부의 객체가 직접적으로 객체의 상태를 주무를 수 없다면 간접적으로 객체의 상태를 변경하거나 조회할 수 있는 방법이 필요하다.

행동

객체의 상태는 저절로 변경되지 않는다. 객체의 상태를 변경하는 것은 객체의 자발적인 행동뿐이다. 객체가 취하는 행동은 객체 자신의 상태를 변경시킨다. 객체의 행동에 의해 객체의 상태가 변경된다는 것은 행동이 부수 효과(side effect)를 초래한다는 것을 의미한다. 부수 효과의 개념을 이용하면 객체의 행동을 상태 변경의 관점에서 쉽게 기술할 수 있다.

객체의 행동은 객체의 상태를 변경시키지만 행동의 결과는 객체의 상태에 의존적이다. 음료를 마신 후의 앨리스의 키는 음료를 마시기 전의 앨리스의 키보다 작아져야 한다. 이것은 음료를 마시는 행동의 결과가 앨리스의 키에 의존한다는 것을 의미한다.

따라서 상태와 행동 사이에는 다음과 같은 의존관계가 있음을 알 수 있다.

  • 객체의 행동은 상태에 영향을 받는다.
  • 객체의 행동은 상태를 변경시킨다.

이것은 상태라는 개념을 이용해 행동을 다음의 두 가지 관점에서 서술할 수 있음을 의미하다.

  • 상호작용이 현재의 상태에 어떤 방식으로 의존하는가
  • 상호작용이 어떻게 현재의 상태를 변경시키는가

앨리스 이야기로 돌아가서 문을 통과하는 행동은 과거의 행동을 돌아볼 필요 없이 앨리스의 ‘키’와 ‘위치’ 두가지 상태를 이용해 간단히 서술 할 수 있다.

  • 앨리스의 키가 40센치미터 이하라면 문을 통과할 수 있다.
  • 문을 통과한 후에 앨리스의 위치는 아름다운 정원으로 바뀌어야 한다.

상태를 이용하면 복잡한 객체의 행동을 쉽게 이해할 수 있다.

어떤 객체도 섬이 아니다. 객체는 자신에게 주어진 책임을 완수하기 위해 다른 객체들을 이용하고 다른 객체에게 서비스를 제공한다. 객체는 다른 객체와 적극적으로 상호작용하며 협력하는 객체들의 공동체에 참여하기 위해 노력한다.

객체가 다른 객체와 협력하는 유일한 방법은 다른 객체에게 요청을 보내는 것이다. 객체가 다른 객체와 메시지를 통해서만 의사소통할 수 있다는 것을 기억하라, 객체가 어떤 행동을 하도록 만드는 것은 객체가 외부로 부터 수신한 메시지다.

객체는 협력과정에서 자기 자신의 상태뿐 아니라 다른 객체의 상태 변경을 유발할 수도 있다. 앨리스가 음료를 마시면 앨리스 자신의 키가 작아지는 동시에 앨리스가 먹은 양만큼 음료의 양이 줄어야 한다. 따라서 음료를 마시는 앨리스의 행동은 자기 자신뿐 아니라 음료의 상태 변경도 유발한다.

정리하자면 객체의 행동으로 인해 발생하는 결과는 두 가지 관점에서 설명할 수 있다. 객체의 행동은 이 두 가지 관점의 부수효과를 명확하게 서술해야 한다.

  • 객체 자신의 상태 변경
  • 행동 내에서 협력하는 다른 객체에 대한 메시지 전송
🖋️

이 책에서는 행동을 다음과 같이 정의한다.

행동이란 외부의 요청 또는 수신된 메시지에 응답하기 위해 동작하고 반응하는 활동이다. 행동의 결과로 객체는 자신의 상태를 변경하거나 다른 객체에게 메시지를 전달할 수 있다. 객체는 행동을 통해 다른 객체와의 협력에 참여하므로 행동은 외부에 가시적이어야한다.

현실 세계의 객체와 객체지향 세계에는 중요한 차이점이 있다. 현실 속 앨리스와 음료는 각각 능동, 수동적인 존재이다. 그러나 객체지향의 세계에서 모든 객체는 자신의 상태를 스스로 관리하는 자율적인 존재다. 앨리스 객체의 키를 작게 만드는 것이 앨리스 자신인 것처럼 음료 객체의 양을 줄이는 것은 음료 자신이어야 한다. 따라서 앨리스는 직접적으로 음료의 상태를 변경 할 수 없다. 단지 음료에게 자신이 음료를 마셨다는 메시지를 전달할 수 있을 뿐이다.

캡슐화가 의미하는 것은 이것이다. 객체는 상태를 캡슐 안에 감춰둔 채 외부로 노출하지 않는다. 객체가 외부에 노출하는 것은 행동 뿐이며, 외부에서 객체에 접근할 수 있는 유일한 방법 역시 행동뿐이다. 객체의 행동을 유발하는 것은 외부로부터 전달된 메시지지만 객체의 상태를 변경할지에 대한 여부는 객체 스스로 결정한다.

상태를 외부에 노출시키지 않고 행동을 경계로 캡슐화하는 것은 결과적으로 객체의 자율성을 높인다. 자율적인 객체는 스스로 판단하고 스스로 결정하기 때문에 객체의 자율성이 높아질 수록 객체의 지능도 높아진다.

결론적으로 상태를 잘 정의된 행동 집합 뒤로 캡슐화 하는 것은 객체의 자율성을 높이고 협력을 단순하고 유연하게 만든다. 이것이 상태를 캡슐화해야 하는 이유다.

식별자

객체란 인간의 인지 능력을 이용해 식별 가능한 경계를 가진 모든 사물을 의미한다. 객체가 식별 가능하다는 것은 객체를 서로 구별할 수 있는 특정한 프로퍼티가 객체 안에 존재한다는 것을 의미한다. 이 프로퍼티를 식별자라고 한다. 모든 객체는 식별자를 가지며 식별자를 이용해 객체를 구별할 수 있다.

값이 같은 지 여부는 상태가 같은지를 이용해 판단한다. 값의 상태가 같으면 두 인스턴스는 동일한 것으로 판단하고 상태가 다르면 두 인스턴스는 다른 것으로 판단한다. 이 처럼 상태를 이용해 두 값이 같은지 판단할 수 있는 성질을 동등성(equality)이라고 한다. 상태를 이용해 동등성을 판단할 수 있는 이유는 값의 상태가 변하지 않기 때문이다.

객체는 시간에 따라 변경되는 상태를 포함하며, 행동을 통해 상태를 변경한다. 따라서 객체는 가변 상태(mutable state)를 가진다고 말한다. 타입이 같은 두 객체의 상태가 완전히 똑같더라도 두 객체는 독립적인 별개의 객체로 다뤄야 한다.

객체는 시간에 따라 변경되는 상태를 포함하며, 행동을 통해 상태를 변경한다. 따라서 객체는 가변 상태(mutable state)를 가진다고 말한다. 타입이 같은 두 객체의 상태가 완전히 똑같더라도 두 객체는 독립적인 별개의 객체로 다뤄야 한다.

식별자를 기반으로 객체가 같은지를 판단할 수 있는 성질을 동일성(identical)이라고 한다.

상태를 기반으로 객체의 동일성을 판단할 수 없는 이유는 시간이 흐름에 따라 객체의 상태가 변하기 때문이다. 따라서 상태가 가변적인 두 객체의 동일성을 판단하기 위해서는 상태 변경에 독립적인 별도의 식별자를 이용할 수밖에 없다.

🖋️

식별자란 어떤 객체를 다른 객체와 구분하는 데 사용하는 객체의 프로퍼티다. 값은 식별자를 가지지 않기 때문에 상태를 이용한 동등성 검사를 통해 두 인스턴스를 비교해야 한다. 객체는 상태가 변경될 수 있기 때문에 식별자를 이용한 동일성 검사를 통해 두 인스턴스를 비교할 수 있다.

대부분의 사람들은 값과 객체의 차이점을 혼란스러워하는데, 대부분 객체지향 프로그래밍 언어에서 두 개념 모두 클래스를 이용해 구현되기 때문이다. 객체지향 프로그래밍 언어를 이용하면 숫자는 Integer 클래스, 사람은 Person 클래스로 정의할 수 밖에 없다. 따라서 객체지향 언어의 관점에서 값과 객체 모두 클래스로부터 생성된 객체이기 때문에 문맥에 따라 그 의미가 혼란스러워 질 수 있다.

객체 식별자의 개념은 상속, 캡슐화, 다형성과 같은 두드러진 개념에 비해 간과하기 쉽다. 그러나 식별자는 객체지향 패려다임의 표현력을 높이는 데 중요한 역할을 한다.

  • 객체는 상태를 가지며 상태는 변경가능하다.
  • 객체의 상태를 변경시키는 것은 객체의 행동이다.
    • 행동의 결과는 상태에 의존적이며 상태를 이용해 서술할 수 있다.
    • 행동의 순서가 실행 결과에 영향을 미친다.
  • 객체는 어떤 상태에 있더라도 유일하게 식별 가능하다.

이 목록은 지금까지 살펴본 객체의 특성을 간략하게 요약한 것이다. 눈썰미가 좋다면 목록이 앞에서 앨리스의 특성을 요약한 목록에서 ‘앨리스’라는 단어를 ‘객체’라는 단어로 살짝 바꿔 놓았을 뿐이라는 것을 눈치 챘을 것이다.

기계로서의 객체

객체지향 개발자의 주된 업무는 객체의 상태를 조회하고 객체의 상태를 변경하는 것이다. 전자를 쿼리(query)라고 하고 후자를 명령(command)라고 한다.

행동이 상태를 결정한다.

  • 상태를 먼저 결정할 경우 캡슐화가 저해된다. 상태에 초점을 맞출 경우 상태가 객체 내부로 깔끔하게 캡슐화 되지 못하고 공용 인터페이스에 그대로 노출되버릴 확률이 높아진다.
  • 객체를 협력자가 아닌 고립된 섬으로 만든다. 상태를 먼저 고려하는 방식은 협력이라는 문맥에서 멀리 벗어난 채 객체를 설계하게 함으로써 자연스럽게 협력에 적합하지 못한 객체를 창조한다.
  • 객체의 재사용성이 저하된다. 객체의 재사용성은 다양한 협력에 참여하기 어렵기 때문에 재사용성이 저하될 수 밖에 없다.

협력에 참여하는 객체를 만들기 위해 가장 중요한 덕목은 행동에 초점을 맞추는 것이다. 행동은 객체가 협력에 참여하는 유일한 방법이다. 따라서 객체가 적합한지 결정하는 것은 객체의 상태가 아니라 행동이다.

객체 지향 설계는 다음과 같은 순서로 수행된다.

  1. 에플리케이션에 필요한 협력을 고민한다.
  2. 협력에 참여하는 데 필요한 행동을 생각한다.
  3. 행동을 수행할 객체를 선택한다.

행동을 결정한 후에야 행동에 필요한 정보가 무엇인지를 고려하게 된다. 따라서, 객체의 행동을 결정하고 그 후에 행동에 적절한 상태를 선택한다.

협력 안에서 객체는 어떤 책임이 필요한가를 결정하는 과정이 전체 설계를 주도한다. “행동이 상태를 결정한다.”

은유와 객체

안타깝게도 객체지향 세계는 현실 세계의 단순한 모방이 아니다. 소프트웨어 안에 구현된 상품 객체는 실제 세계의 상품과는 전혀 다른 양상을 띤다. 모방과 추상화라는 개념만으로는 현실 객체와 소프트웨어 객체 사이의 관계를 깔끔하게 설명하지 못한다.

현실 객체와 소프트웨어 객체 차이의 가장 큰 차이점은 전자에서 수동적인 존재가 후자에서 구현될 때 능동적으로 변한다는 점이다. 현실 속의 트럼프 카드는 스스로 뒤집을 수도, 말 할 수도, 걸을 수도 없다. 전화기는 스스로 통화 버튼을 누를 수 없고 계좌는 스스로 송금할 수 없다. 레베카 워프스브록은 이를 객체의 의인화(anthropomorphism)라고 한다.

객체지향 설계자로서 우리의 목적은 현실의 모방이 아니다. 단지 이상한 나라를 창조하면 된다. 현실을 닮아야 한다는 어떤 제약이나 구속도 없다. 창조한 객체의 특성을 상기시킬 수 있다면 현실 속의 객체 이름을 이용해 객체를 묘사하라.

Comments

객체는 상태(state), 행동(behavior), 식별자(identity)를 지닌 실체이다.

  • 상태 : 특정 시점에 객체가 가진 정보의 집합, 객체의 구조적 특징을 표현. 상태를 이용하면 과거의 모든 행동 이력을 설명하지 않고도 행동의 결과쉽게 예측하고 설명할 수 있다. 객체의 프로퍼티는 단순한 값과 객체를 참조하는 링크로 구분 가능하다.
  • 행동 : 외부의 요청 또는 수신된 메시지에 응답하기 위해 동작하고 반응하는 활동. 객체 자신의 상태를 변경하거나 다른 객체에게 메시지를 전달 할 수 있어야 한다.
  • 식별자 : 객체를 다른 객체와 구분하는 데 사용하는 객체의 프로퍼티. 객체는 상태가 변경될 수 있기 때문에 식별자를 이용한 동일성 검사를 통해 두 인스턴스를 비교할 수 있다.

현실세계와 객체 지향의 가장 중요한 차이점은 수동적인 객체("사물이라고 불러지는")가 능동적으로 변한다는 것이다.

객체의 상태는 결국 이벤트 소싱에서 snapshot을 만드는 아이디어와 연결 가능하다. 대량의 이벤트를 저장하고 읽기 시 매번 모든 이벤트를 실행할 수 없으니, 프로퍼티 값으로 저장한다고 생각한다.

객체는 행동이 우선이다. 객체의 행동은 외부에 가시적이어야 하며, 객체는 오직 행동으로 수신, 응답한다. 객체는 행동의 결과로 상태를 변경한다.

모든 업무의 역할은 결국 조회(query)와 명령(command)으로 나뉜다.

이는 react-query (opens in a new tab)와 같은 상태관리 라이브러리나 cqrs (opens in a new tab)와 같은 패턴에서도 잘 드러난다.