5 분 소요

[Chap 05] 책임과 메시지

자율적인 책임

자율성의 사전적 의미는 ‘자기 스스로의 원칙에 따라 어떤 일을 하거나 자신을 통제해서 절제하는 성질이나 특성’이다.

여기서 객체가 어떤 행동을 하는 유일한 이유는 다른 객체로부터 요청을 수신했기 때문이다. 하지만 여기서 수신한 내용은 “어떤” 행동을 할지이지, “어떻게” 행동할지에 대한 요청을 수신받은 것이 아니다.
이 책에서 말하는 자율적인 객체란 요청받은 행동에 대해서 “어떻게” 행동할지를 스스로 결정하는 것이다.

적절한 책임이 자율적인 객체를 낳고, 자율적인 객체들이 모여 유연하고 단순한 협력을 낳는다. 적절한 책임이란 앞장에서 말했던 재판장이 모자 장수에게 ‘증언하라’ 라고 요청을 하는 것을 의미한다.
‘증언하라’ 라는 것이 왜 적절한 책임일까? 책임이 자율적이기 때문이다.
그렇다면 반대로 자율적이지 않은 책임은 무엇일까? ‘메모한 내용을 증언하라’, ‘목격했던 장면을 떠올리고, 떠오르는 기억을 시간 순서대로 재구성한 다음에 증언하라’ 등의 책임에 대한 방식까지 결정해주는 것이 자율적이지 않은 책임인 것이다.
자율적이지 않은 책임은 선택할 수 있는 자유의 범위를 지나치게 제한한다.

다시 말해서 자율적인 책임이란 증언 방식이나 증언에 필요한 자료는 스스로의 의지와 판단에 따라 자유롭게 선택할 수 있다는 것이다.
결국, 객체가 자율적이기 위해서는 객체에게 할당되는 책임의 수준 역시 자율적이어야 한다. 책임의 수준이 자율적이기 위해서는 책임 자체가 주상적인 동시에 협력의의도를 뚜렷하게 표현할 수 있을 정도로 충분히 구체적이어야 한다.

자율적이고 추상적인 개념이란 한마디로 ‘무엇’을 해야 하는지만 결정하는 것이지, ‘어떻게’ 해야 하는지에 대해서는 전혀 언급하지 않는 것이다.

메시지와 메서드

앞 장에서도 설명했던 것처럼 메시지란 객체가 다른 객체에게 책임을 요청할 때 선택할 수 있는 의사소통 방법이다.
메시지의 형태는 메시지 이름(요청 내용), 인자(추가적인 정보)이고, 메시지 전송을 위해서는 수신자, 메시지 이름, 인자의 조합이 된다.

메시지가 객체지향에서 굉장히 중요한 이유는 객체와 다른 객체 사이의 연결을 약하게 한다는 점이다. 다른 객체에게 ‘무엇’을 하라고만 요청을 하기 때문에 요청한 객체는 요청한 객체가 ‘어떻게’ 행동을 하는지 모른다. 즉, 책임을 수행하는 방법을 변경하더라도 요청한 객체는 그 사실을 알 수 없다.

객체가 유일하게 이해할 수 있는 의사소통 수단은 메시지 뿐이며 객체는 메시지를 처리하기 위한 방법을 자율적으로 선택할 수 있다.
여기서 메시지를 처리하기 위한 방법을 메서드라고 한다.

다형성

메시지와 메서드의 구체적인 차이를 이해했다면 다형성의 의미를 이해하기 쉬워진다. 다형성이란 서로 다른 유형의 객체가 동일한 메시지에 대해 서로 다르게 반응하는 것을 의미한다. 그렇기 때문에 동일한 메시지를 이해할 수 있는 객체라면 다르게 반응하더라도 책임을 수행할 수 있는 대체 가능성을 의미한다.
앨리스의 예시로 들자면 왕은 판사의 역할이고 모자 장수는 증인의 역할이다. 증인의 역할을 할 수 있는 다른 객체가 있다면 판사는 증인이 누구든 상관없이 메시지를 수신할 객체의 타입을 자유롭게 추가할 수 있다.

즉, 다형성은 송신자와 수신자 간의 객체 타입에 대한 결합도를 메시지에 대한 결합도로 낮춤으로써 달성된다. 이렇게 된다면 객체지향이 유연하고 확장 가능하고 재사용성이 높아질 수 있다.

  • 협력이 유연하다
    송신자는 수신자가 메시지를 이해한다면 누구라고 상관하지 않는다.
  • 확장 가능하다
    송신자에게 아무런 영향 없이 수신자를 바꿀 수 있기 때문에 협력을 확장하고 싶다면 새로운 유형의 객체를 협력에 끼워 맞추기만 하면 된다.
  • 재사용이 가능하다
    협력에 영향을 미치지 않고 수신자의 자리를 대체할 수 있기 때문에 다양한 문맥에서 협력을 재사용할 수 있다.

메시지를 따라라

객체지향의 강력함은 객체들이 주고받는 메시지로부터 나온다. 전에도 객체지향에서 중요한 것은 클래스가 아니라 객체라고 했었고, 객체의 행동이 중요하다고 했었다.
근데 객체의 행동이 중요하다고 하는 것은 우선적으로 메시지가 중요하다는 것이다. 결국 객체는 메시지를 바탕으로 ‘어떻게’ 행동을 할 지 결정하기 때문이다.

따라서 객체들의 속성과 행위를 식별하는 것이 먼저다. 클래스는 객체의 속성과 행위를 담는 틀일 뿐이다. 객체지향 패러다임으로의 전환은 메시지를 주고받는 동적인 객체들의 집합으로 바라보는 것에서 시작된다.

협력 관계 속에서 다른 객체에게 무엇을 제공해야 하고 다른 객체로부터 무엇을 얻어야 하는가라는 관점에서 접근할 때만 훌륭한 책임을 수확할 수 있다. 독립된 객체의 상태와 행위에 대해 고민하지 말고 시스템의 기능을 구현하기 위해 객체가 다른 객체에게 제공해야 하는 메시지에 대해서 고민하는 것이 중요하다.

책임-주도 설계 다시 살펴보기

책임을 완수하기 위해 협력하는 객체들을 이용해 시스템을 설계하는 방법을 책임-주도 설계라고 한다. 이는 객체들 간에 주고받는 메시지를 기반으로 적절한 역할과 책임, 협력을 발견하는 것이다.

따라서 객체가 책임을 완수하기 위해 다른 객체의 도움이 필요하다고 판단되면

  1. 도움을 요청하기 위해 어떤 메시지가 필요한지 결정
  2. 메시지를 수신하기에 적합한 객체를 선택
  3. 수신된 메시지가 객체의 책임을 결정

이러한 방식을 What/Who 사이클 이라고 한다.
What: 무슨 메시지가 필요한지
Who: 그 메시지를 수신하게 적합한 객체가 누구인지

What을 우선적으로 선택하고 그 다음 Who를 선택하게 된다면 메시지를 이용하는 책임-주도 설계의 핵심 아이디어를 명확하게 표현할 수 있다.

또한 메시지 중심 설계(책임-주도 설계)을 위해서는 묻지 말고 시켜라 스타일을 적용시켜야 한다.
그렇게 된다면 객체지향 어플리케이션이 자율적인 객체들의 공동체라는 사실을 강조할 수 있다.

왜? => 메시지를 송신하고자 하는 객체가 수신하려는 객체에 대해서 너무 많은 정보를 알게 되면 송신자와 수신자의 결합이 강해진다. 결합이 강해진다면 대체 가능성이 떨어지고 유연하지 않으며 확장 하기 위해서는 수정해야되는 부분이 많고, 재사용성도 떨어지게 된다.

결국 묻디 말고 시켜라 스타일은 객체를 자율적으로 만들고 캡슐화를 보장하며 결합도를 낮게 유지시켜 주기 때문에 설계를 유연하게 만든다.

객체 인터페이스

인터페이스라는 단어는 개발자라면 익숙한 단어이다. GUI 를 보게 되면 사용자는 마우스를 이용해서 화면에 그려진 윈도우나 아이콘을 사용한다.
TV를 보기 위해서는 리모컨이라는 인터페이스를 이용하고, 스마트폰을 사용하기 위해서는 손가락이라는 인터페이스가 필요하다.
핸들, 변속기, 엑셀, 브레이크라는 인터페이스를 이용해서 자동차를 운전한다.

예시들을 통해서 인터페이스에 대한 세 가지 특성을 설명할 수 있다.

  1. 인터페이스의 사용법만 알고 있으면 대상의 내부 구조나 동작 방법을 몰라도 상호작용이 가능하다.
  2. 인터페이스가 변경되지 않고 단순히 내부 구성이나 작동 방식이 변경되는 것은 인터페이스 사용자에게 아무런 영향도 미치지 않는다.
  3. 인터페이스가 동일하기만 한다면 어떤 대상과도 상호작용할 수 있다.

위에서 객체가 다른 객체와 상호작용할 수 있는 유일한 방법은 ‘메시지 전송’ 이라고 했다. 따라서 객체의 인터페이스는 객체가 수신할 수 있는 메시지의 목록으로 구성되며 객체가 어떤 메시지를 수신할 수 있는지가 객체가 제공하는 인터페이스의 모양을 빚는다.

인터페이스와 구현의 분리

인터페이스의 중요한 원칙 세 가지가 있다.

  1. 좀 더 추상적인 인터페이스
    ‘증언하라’ 라는 메시지처럼 추상적인 인터페이스를 제공해야 자율적인 객체를 설계할 수 있다.
  2. 최소 인터페이스
    외부에서 사용할 필요가 없는 인터페이스는 최대한 노출하지 말아라
  3. 인터페이스와 구현 간의 차이가 있다는 점을 인식
    인터페이스는 위에서 봤던 것처럼 외부 객체와 메시지를 송수신하기 위해서 필요하다. 구현은 내부 구조와 작동 방식을 가리키는 고유의 용어이다.

구현

객체의 외부와 내부를 분리하라는 것은 결국 객체의 공용 인터페이스와 구현을 명확하게 분리하라는 말과 동일하다.

구현을 수정하더라도 공용 인터페이스 사용에 아무런 문제가 되지 않도록 만들기 위해서는 적절한 구현을 선택하고 이를 인터페이스 뒤로 감추는 것을 말한다.
구현에는 객체가 가져야 할 상태와 메서드 구현은 객체 내부에 속한다. 적절한 구현을 하게 된다면 객체 외부에 영향을 미치는 변경은 객체의 공용 인터페이스를 수정할 때뿐이다.

인터페이스와 구현의 분리가 완벽하게 되었다는 것은 캡슐화 를 통해서 정보 은닉에 성공했다는 말이다!

책임의 자율성이 협력의 품질을 결정한다.

  1. 자율적인 책임은 협력을 단순하게 만든다.
    자율적인 책임은 세부적인 사항들을 무시하고 의도를 드러내는 하나의 문장으로 표현함으로써 협력을 단순하게 만든다.
  2. 자율적인 책임은 모자 장수의 외부와 내부를 명확하게 분리한다.
    왕은 모자 장수가 외부에 노출한 책임은 볼 수 있지만 모자 장수가 내부적으로 어떻게 책임을 수행하는지는 볼 수 없다.
  3. 책임이 자율적일 경우 책임을 수행하는 내부적인 방법을 변경하더라도 외부에 영향을 미치지 않는다.
  4. 자율적인 책임은 협력의 대상을 다양하게 선택할 수 있는 유연성을 제공한다.
  5. 객체가 수행하는 책임들이 자율적일수록 객체의 역할을 이해하기 쉬워진다. 책임이 자율적일수록 객체의 존재 이유를 명확하게 표현할 수 있다. 즉, 책임이 자율적일수록 객체의 응집도를 높은 상태로 유지하기가 쉬워진다.

결과적으로 자율적인 책임 은 굉장히 중요한 것이다. 옳바른 자율적인 책임을 결정하는 것은 협력이 이해하기 쉬워지고, 객체의 외부와 내부의 구분이 명확해지며, 변경에 의한 파급효과를 제한할 수 있고, 유연하게 변경할 수 있는 동시에 다양한 문맥에서 재활용할 수 있게 된다.

책임이 자율적일수록 적절하게 ‘추상화’되며, ‘응집도’가 높아지고, ‘결합도’가 낮아지며, ‘캡슐화’가 증진되고, ‘인터페이스와 구현이 명확히 분리’되며, 설계의 ‘유연성’과 ‘재사용성’이 향상된다.

정리

추상화, 응집도, 결합도, 캡슐화, 인터페이스와 구현이라는 어려운 단어를 자율적인 책임 을 기준으로 설명한 장이었다.

자율적인 책임(무엇) -> 추상적인 인터페이스, 최소 인터페이스를 전달 -> 이에 맞는 객체 선택 -> 구현(메서드)를 통해서 어떻게 응답을 할지를 결정
이런식으로 설계가 진행된다면(책임-주도 설계 내에 What/Who 사이클)

  1. 인터페이스와 구현이 분리
    -> 캡슐화(정보 은닉) 가능
    -> 내부의 내용을 바꾸어도 외부에 영향 X(객체간의 결합도가 느슨해진다.)
    -> 유연하고 확장 가능하며 재사용이 가능한 설계 가능
  2. 객체의 자율성 보장 -> 외부 객체는 무엇에 대한 책임만 요청했지 어떻게에 대한 것은 요청하지 않음
  3. 응집도가 높아짐

태그:

카테고리:

업데이트:

댓글남기기