Close
Close full mode
logo만렙 개발자 키우기

5장. 책임과 메시지

Git RepositoryEdit on Github
Last update: 9 months ago by nowwaterReading time: 8 min

훌륭하고 성장 가능한 시스템을 만들기 위한 핵심은 모듈 내부의 속성과 행동이 어떤가보다 모듈이 어떻게 커뮤니케이션하는가에 달려 있다.

자율적인 책임

설계의 품질을 좌우하는 책임

객체지향 공동체를 구성하는 기본 단위는 자율적인 객체다.

여기서 키워드는 자율성이고, 자율적인 객체란 스스로 정한 원칙에 따라 판단하고 스스로의 의지를 기반으로 각자 맡은 책임을 수행하는 객체이다.

즉, 객체들은 애플리케이션의 기능을 구현하기 위해 협력하고, 협력 과정에서 각자 맡은 바 책임을 다하기 위해 자율적으로 판단하고 행동한다.

책임 : 요청을 처리하기 위해 객체가 수행하는 행동

객체지향 설계의 아름다움은 적절한 책임을 적절한 객체에게 할당하는 과정 속에서 드러난다.

객체지향 애플리케이션을 설계하는 가장 널리 알려진 방법을 책임-주도 설계라고 부르는 이유는 적절한 책임의 선택이 전체 설계의 방향을 결정하기 때문이다.

협력에 참여하려면 요청을 적절하게 처리한 후 응답해야 한다. 어떻게 처리하는 지는 신경쓰지 않는다. 방식은 스스로의 의지와 판단에 따라 자율적으로 선택할 수 있다. 따라서 책임은 포괄적이고 추상적이다.

너무 추상적인 책임

포괄적이고 추상적인 책임을 선택한다고 해서 무조건 좋은 것은 아니다. 즉, 너무 구체적인 것과 너무 추상적인 것은 좋지 않다.

이때 추상적이고 포괄적인 책임은 협력을 좀 더 다양한 환경에서 재사용할 수 있도록 유연성을 제공한다. 책임은 협력에 참여하는 의도를 명확하게 설명할 수 있는 수준 안에서 추상적이어야 한다.

그리고 책임이 자율적인지를 판단하는 기준은 문맥에 따라 다르기 때문에, 현재 문맥에 가장 적합한 책임을 선택할 수 있는 안목을 기르는 것이 중요하다.

'어떻게'가 아니라 '무엇'을

자율적인 책임의 특징은 객체가 어떻게 해야 하는가가 아니라 무엇을 해야 하는가를 설명한다는 것

책임을 자극하는 메시지

책임이라는 말 속에는 어떤 행동을 수행한다는 의미가 포함돼 있다. 객체는 다른 객체로부터 요청을 수신할 때만 어떤 행동을 시작한다. 따라서 객체가 자신의 책임을 수행하도록 시키는 것은 외부에서 전달되는 요청이다.

사실 객체가 다른 객체에 접근할 수 있는 유일한 방법은 요청을 전송하는 것 뿐. 이 요청을 메시지라고 부른다.


메시지와 메서드

메시지

하나의 객체는 메시지를 전송함으로써 다른 객체에 접근한다. -> 유일한 의사소통 방법

  • 메시지 이름 : 메시지를 가리키는 부분

  • 메시지 인자 : 메시지 전송 시 추가적인 정보가 필요할 경우 제공하는 내용

앨리스 상황 예시) 모자장수.증언하라(어제, 왕국)

메시지를 수신받은 객체는 우선 자신이 해당 메시지를 처리할 수 있는지 확인한다. 메시지를 처리할 수 있다면, 객체가 해당 메시지에 해당하는 행동을 수행할 책임이 있다는 것을 의미한다.

수신할 수 있는 메시지 => 수행해야 할 책임의 형태를 결정

객체는 어떤 메시지를 전송할 수 있는지다른 객체가 어떤 메시지를 처리해줄 수 있는지만 알 수 있다. 메시지를 처리하기 위해 책임을 수행하는 방법은 외부의 다른 객체가 볼 수 없는 객체 자신의 사적인 영역에 속한다.

=> 외부 객체는 메시지에 관해서만 볼 수 있고, 객체 내부는 볼 수 없다.

메서드

메시지를 처리하기 위해 내부적으로 선택하는 방법을 메서드라고 한다.

1. 객체는 메시지를 수신하면 먼저 해당 메시지를 처리할 수 있는지 여부를 확인

2. 처리할 수 있다고 판단되면 자신에게 주어진 책임을 다하기 위해 메시지를 처리할 방법인 메서드를 선택

  • 메서드는 클래스 안에 포함된 함수 또는 프로시저를 통해 구현

어떤 객체에게 메시지를 전송하면 결과적으로 메시지에 대응되는 특정 메서드가 실행된다.

메시지는 어떻게 수행될 것인지는 명시하지 않는다. 단지 오퍼레이션을 통해 무엇이 실행되기를 바라는지만 명시한다. 어떤 메서드를 선택할 지는 전적으로 수신자의 결정에 좌우

이렇게 메시지를 수신한 객체가 실행 시간에 메서드를 선택할 수 있다는 사실은 다른 프로그래밍 언어와 객체지향 프로그래밍 언어를 구분 짓는 핵심적인 특징 중 하나이다. (절차적인 언어는 컴파일 타임에 실행 코드를 결정)

다형성

다형성이란 서로 다른 유형의 객체가 동일한 메시지에 대해 서로 다르게 반응(서로 다른 메소드)하는 것을 의미한다.

메시지는 무엇이 실행될지 명시, 메소드는 어떻게 실행할 지 결정. 따라서 동일한 메시지라도 서로 다른 방식의 메소드를 이용해 처리할 수 있다.

따라서 다형성은 하나의 메시지 + 하나 이상의 메서드 이다.

서로 다른 객체들이 다형성을 만족시킨다는 것은 객체들이 동일한 책임을 공유한다는 것을 의미한다.

송신자의 관점에서 다형적인 수신자들을 구별할 필요가 없으며, 자신의 요청을 수행할 책임을 지닌다는 점에서 모두 동일

앨리스 상황 예시) 왕의 증언하라 메시지 => 모자 장수, 요리사, 앨리스는 모두 각각 다른 내용으로 처리한다.(증인 역할 수행)

다형성은 기본적으로 동일한 역할을 수행할 수 있는 객체들 사이의 대체 가능성을 의미한다. 왕의 관점에서 각 인물들은 동일한 메시지를 처리할 수 있기 때문에 서로 대체가 가능하다.

다형성은 이러한 객체들의 대체 가능성을 이용해 설계를 유연하고 재사용 가능하게 만든다. 즉, 송신자가 수신자의 종류를 모르더라도 메시지를 전송할 수 있다. => 수신자의 종류를 캡슐화

다형성은 객체 타입에 대한 결합도메시지에 대한 결합도로 낮춤으로써 달성된다. 이를 통해 객체간 협력을 유연하고 확장 가능한 구조로 만들어준다. => 객체지향 패러다임이 강력한 이유


이렇게 송신자가 수신자에 대해 매우 적은 정보만 알아도 상호 협력이 가능하다는 사실은 설계의 품질에 큰 영향을 미친다.

1. 협력이 유연해진다.

  • 송신자는 수신자가 메시지를 이해한다면 누구라도 상관없다.

  • 송신자에 대한 파급효과 없이 유연하게 협력을 변경할 수 있다.

2. 협력이 수행되는 방식을 확장할 수 있다.

  • 송신자에게 아무 영향없이 수신자를 교체할 수 있어서 협력의 세부적인 수행 방식을 쉽게 수정할 수 있다.

3. 협력이 수행되는 방식을 재사용할 수 있다.

  • 협력에 영향없이 다양한 객체들이 수신자의 자리를 대체 가능 => 다양한 문맥에서 협력 재사용 가능

  • ex) 재판 이라는 협력을 모자 장수, 앨리스, 요리사... 모든 곳에서 재사용 가능

송신자-수신자를 약하게 연결하는 메시지

유연하고, 확장 가능하고, 재사용성이 높은 협력 => 다형성을 지탱하는 메시지가 존재하기 때문에 가능

메시지는 송/수신자 사이의 결합도를 낮춤으로써 설계를 유연하고, 확장 가능하고, 재사용 가능하게 만든다.

  • 송신자는 수신자가 메시지를 이해하고 처리해 줄 것이라는 사실만 알아도 충분
  • 수신자는 메시지 처리를 위해 자율적으로 메서드 선택. But 메서드 자체는 송신자에게 노출하지 않는다.

메시지를 따라라

객체지향의 핵심: 메시지

실제로 애플리케이션을 살아있게 만드는 것은 클래스가 아니라 객체다. 그리고 이런 객체들의 윤곽을 결정하는 것이 바로 객체들이 주고받는 메시지다.

클래스는 단지 동적인 객체들의 속성과 행위를 정적인 텍스트로 표현하기 위해 사용할 수 있는 추상화 도구다. 따라서 객체들의 속성과 행위를 식별하는 것이 클래스를 정의하는 것보다 우선된다.

클래스는 객체의 속성과 행위를 담는 틀일 뿐. 클래스를 중심에 두는 설계는 유연하지 못하고 확장하기 어렵다. 클래스에 담길 객체들의 공통적인 행위와 속성을 포착하기 위해서는 먼저 협력하는 객체들의 관점에서 시스템을 바라봐야 한다.

그러나 객체 자체에 초점을 맞출 경우 가장 흔한 실수는 협력이라는 문맥을 배제한 채 객체 내부의 데이터 구조를 먼저 생각한 후 데이터 조작에 필요한 오퍼레이션을 나중에 고려하는 것이다.

그렇게 되면 객체 내부 구조를 객체 정의의 일부로 만들기 때문에 객체의 자율성을 저해한다. 객체의 내부 구조는 감춰져야 한다.

결국 객체를 이용하는 중요한 이유는 객체가 다른 객체가 필요로 하는 행위를 제공하기 때문이다. 즉, 협력 관계 속에서 다른 객체에게 무엇을 제공해야 하고 다른 객체로부터 무엇을 얻어야 하는가라는 관점에서 접근할 때만 훌륭한 책임을 수행할 수 있다.

독립된 객체의 상태와 행위에 대해 고민하지 말고, 시스템 기능 구현을 위해 다른 객체에게 제공해야 하는 메시지에 대해 고민해라

협력의 문맥에서 벗어나 독립된 객체에 관해 고민하는 것은 클래스에 초점을 맞추는 것과 별다른 차이가 없다.

객체지향 설계의 중심에는 메시지가 위치한다.

즉, 메시지가 객체를 선택하게 해야 한다.

객체지향 설계는 적절한 책임을 적절한 객체에게 할당하면서 메시지를 기반으로 협력하는 객체들의 관계를 발견하는 과정이다.

객체가 책임을 완수하기 위해 다른 객체의 도움이 필요하다고 판단되면 도움을 요청하기 위해 어떤 메시지가 필요한지 결정한다. 메시지를 결정한 후에는 메시지를 수신하기에 적합한 객체를 선택한다. 결과적으로 메시지가 수신자의 책임을 결정한다.

What/Who 사이클

어떤 행위가 필요한지를 먼저 결정한 후에 이 행위를 수행할 객체를 결정한다.

수신 가능한 메시지가 모여 객체의 인터페이스를 구성한다.

송신자는 수신자가 어떤 객체인지는 모르지만 자신이 전송한 메시지를 잘 처리할 것이라는 것을 믿고 메시지를 전송한다.

결과적으로 객체를 자율적으로 만들고 캡슐화를 보장하며 결합도를 낮게 유지시켜 주기 때문에 설계를 유연하게 만들어준다.

즉, 송신자와 수신자 간에 결합도가 낮아져 설계가 유연해지고 의도 역시 명확해진다.

객체가 자신이 수신할 메시지를 결정하지 말고, 메시지가 협력에 필요한 객체를 발견하게 해야 한다.

Previous
4장. 역할, 책임, 협력
Next — 📖 Read Book
Clean Code