[객체지향의 사실과 오해] 5장

2020. 4. 22. 15:33책/객체지향의 사실과 오해

5장 책임과 메시지

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


존 달리와 밥 라타네의 실험

1964년 두 명의 젊은 심리학자는 흥미로운 실험을 하게 된다.

학생들을 모집하여 도시 생활에 대한 어려움을 이야기 하게끔 한 이 실험은,

학생들을 물리적으로 격리 시켜 두었지만 오디오 시스템을 통해 다른 학생의 이야기를 들을 수 있게 설계되었다.

그러나 실제 실험에서 참가자 본인을 제외한 다른 학생들의 목소리는 미리 녹음된 내용을 재생한 것이었다.

학생들은 다른 학생이 각자의 방에서 실시간으로 이야기를 하고 믿고 있는 상태였다.

실험의 주요 포인트는 간질을 앓고있는 학생의 녹음본이 재생될 때 시작된다.

간질을 앓고있는 학생의 발작이 시작되어 도움을 요청하는 오디오가 재생 되었을때 학생들의 반응을 본 것이다.

놀랍게도 약 31퍼센트만이 도움을 주기 위한 행동을 취했다고 한다. (심리학자들에게 도움을 요청하는 등의 행동)

그렇다면, 나머지 70퍼센트의 학생들은 인성에 문제가 있는 것이 었을까?

이 실험에서 학생들은 자신 외에도 발작을 일으키고 도움을 요청하는 사운드를 듣고있는 사람이 다수가 존재한다고 믿었다.

실험을 달리하여 간질 환자와 자신만이 실험에 참여하고 있다고 설계된 실험에서는 85퍼센트가 넘는 학생들이 도움을 주기 위한 행동을 취했다.

즉, 사건에 대한 목격자가 많을수록 책임감이 분산된다는 것이었다.

이 실험은 어떠한 위기 상황에서 명확한 책임을 가진 권위자가 없을 때 어떤 일이 벌어지는지를 잘 보여주었다.

또한 이 이야기가 들려주는 교훈은 명확한 책임과 역할을 지닌 참가자들이 협력에 참여해야 한다는 것이다.

어딘가 객체지향 세계와 닮아 있다. 훌륭한 객체지향의 세계 또한 명확하게 정의된 역할과 책임을 지닌 객체들이 상호 협력하는 세계일 것이다.

 

자율적인 책임

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

자율적인 객체란 스스로 정한 원칙에 따라 판단하고 스스로의 의지를 기반으로 행동하는 객체를 뜻한다.

객체가 어떠한 행동을 하는 유일한 이유는 다른 객체로부터 요청을 수신하는 것인데, 이 요청을 우리는 메시지라고 부른다.

그리고 이 메시지를 받아 처리하기 위해 객체가 수행하는 행동을 '책임'이라고 부른다. 

요약하자면, 자율적인 객체란 자율적으로 책임을 수행하는 객체이다.

책임을 자율적으로 수행하려면 어떻게 해야할까? 

책임을 수행하게 만드는 요청의 내용이 너무 구체적이지 않아야한다.

앨리스이야기의 재판내용으로 예를 들어보자면, 왕이 모자장수에게 '증언하라' 라고 요청을 한다. 

모자장수는 증언하라는 요청에 자신이 원하는 방식으로 증언을 할 수 있다. 즉, 어떤 방식으로 증언을 할지 스스로 결정할 수 있다. 

만약, 왕이 모자장수에게 '목격했던 장면을 떠올린후 시간순서대로 재구성하여 말로 간결하게 표현하라' 라고 요청을 한다면

모자장수는 증언방식을 스스로 정할 수 없게 된다. 즉, 책임을 자율적으로 수행하는 것이 아니라 요청에 맞게 수행하게 되는 것이다. 

이러한 상황은 모자장수를 자율적이지 못한 협력객체로 만들어 버리고 이것은 전체적으로 시스템의 유연함을 저하시킬것이다. 
왕의 입장에서는 어떤방식으로 증언하는지는 중요하지 않다. 단지, 증언의 정보가 중요한 것이기 때문에 그 방식까지 정할 필요가 전혀 없는 것이다. 증언의 구체적인 방법이나 절차는 모자 장수가 자유롭게 선택하도록 허용하는 것이 좀더 유연하고 자율적인 과정일 것이다.

반대로, 너무 추상적인 책임을 요청하는 것 또한 문제가 된다. '증언하라'가 아니라 '설명하라' 라고 요청했을 경우 

모자장수는 어떠한 것을 설명하라는 것인지 알지 못하게 된다. 

이 일화를 통해 나는 너무 추상적이지도, 너무 구체적이지도 않은 책임을 객체에게 요청하는 것이 얼마나 중요한지를 한번에 깨달은 것 같다. 

이러한 좋은 설계를 하기 위해서는 '어떻게'가 아니라 '무엇'을 해야하는가를 책임으로 정해야 할 것이다.

 

메시지와 메서드

계속해서 되새기고 있는 내용은 객체들이 서로 통신할 수 있는 방법은 메시지 뿐이라는 사실이다.

이것을 계속해서 되새기는 이유는 아마, 이 얇은 실같은 것이 객체들을 이어주는 유일한 것이기 때문이다.

이것은 객체간의 약한 결합을 의미하는데, 이것이 주는 강력한 이점들은 계속해서 와닿게 될 것 같다.

다시.... 객체는 메시지를 전송함으로써만 다른 객체에 접근할 수 있다 !!

이러한 메시지를 수신할 수 있다는 것은 객체가 메시지에 해당하는 책임을 수행할 수 있다는 것을 의미할 것이다.

즉, 메시지는 '무엇'을 하라는 책임을 담고 있다.

그렇다면 '무엇'을 하라는 메시지를 받은 수신자는 이것을 '어떻게' 처리할지 결정해야 하는데,

그것이 바로 개발을 경험해본 사람에게는 친숙한 메서드 이다.

앨리스의 일화에서 왕이 모자장수에게 '증언하라'라는 메시지를 보냈을때, 모자장수는 '어떻게'증언할지를 선택하게 되는데 

이 선택지들이 바로 메서드 인 것이다.

'무엇'에 해당하는 메시지와 '어떻게'에 해당하는 메서드를 구분하여 이해함으로써,

다형성이라는 객체지향 세계에서 아주 의미있는 내용을 쉽게 이해 할 수 있게 되었다.

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

즉, 서로 다른 타입에 속하는 객체들이 동일한 메시지를 수신했을 때, 서로 다른 메서드를 이용해 메시지를 처리할 수 있는 메커니즘을 뜻하는 것이다.

이것은, 메시지를 수신하는 객체들이 동일한 책임을 공유한다고 볼 수도 있겠다.

이러한 다형성은 객체들의 대체 가능성을 이용하여 설계를 유연하고 재사용 가능하게 만들어준다.

왕의 입장에서 증언을 하는 대상이 모자 장수이던, 앨리스이던, 요리사이던 상관이 없는 것처럼,

메시지를 송신하는 객체 입장에서는 수신하는 객체의 타입이 중요하지 않게 되는 것이다.

이것은 메시지를 처리할 수 있는 어떤 객체와도 협력할 수 있는 유연하고 확장 가능한 구조를 만들어 준다.

이렇게 송신자가 수신자에 대해 매우 적은 정보만 알더라도 협력이 가능하다는 사실은 객체지향의 설계의 품질에 큰 영향을 미칠 수 있겠다.

첫째로, 메시지를 처리할 수 만 있다면 수신자에 대한 제약이 없기 때문에 협력이 아주 유연하게 이루어 질 수 있을 것 같다.

둘째로, 협력이 수행되는 과정을 쉽게 확장할 수 있다. 송신하는 객체의 변화없이 수신자의 다양한 메서드를 협력에 이용할 수 있기 때문이다.

셋째로, 협력이 수행되는 방식을 재사용 할 수 있게 되는 것이다. '재판'이라는 협력은 '증언하라'라는 메시지와 그것을 처리하는 메서드로 이루어 지는 것인데, 송신자와 수신자가 유연하게 바뀔 수 있으므로 '재판'이 아닌 다른 협력에도 쉽게 재사용 될 수 있다는 의미이다. 

내가 기억할 것은 결국, 이 모든것이 '메시지'라는 얇은 실로 이어진 객체들 간의 낮은 결합도 덕분이라는 것이다.

따라서 설계의 품질을 높이기 위해서는 이 '메시지'를 심사숙고하여 선택해야 겠다.

 

What/Who 사이클

책임-주도 설계의 핵심은 어떤 행위가 필요한지를 먼저 결정한 후에, 이 행위를 수행할 객체를 결정하는 것이다.

즉, 객체 사이의 협력 관계를 설계하기 위해서는 먼저 '어떤 행위(what)'를 수행할 것인지를 결정한 후에 

'누가(who)' 그 행위를 수행할 것인지를 결정해야 한다는 것이다. 여기서 '어떤행위(what)'가 메시지 이다.

이 사이클은 어떤 객체가 필요한지를 생각하지 말고 어떤 메시지가 필요한지를 먼저 고민하라고 조언해준다.

메시지를 결정하기 전까지는 객체에 관해 고민하지 말아야 한다.

일단 메시지가 결정된 후에야 이 메시지를 처리할 객체를 선택한다는 것이다.

 

객체 인터페이스

인터페이스란 원래 어떤 두 사물이 마주치는 경계 지점에서 서로 상호작용할 수 있게 이어주는 방법이나 장치를 의미한다.

이러한 인터페이스는 일반적으로 세가지 특징을 지닌다고 할 수 있다.

첫쨰, 인터페이스의 사용법을 익히기만 하면 내부 구조나 동작 방식을 몰라도 쉽게 대상을 조작하거나 의사를 전달 할 수 있다.

둘째, 인터페이스 자체는 변경하지 않고 단순히 내부 구성이나 작동 방식만을 변경하는 것은 인터페이스 사용자에게 어떤 영향도 미치지 않는다.

셋째, 대상이 변경되더라도 동일한 인터페이스를 제공하기만 하면 아무런 문제 없이 상호작용 할 수 있다.

 

맷 와이스펠드는 객체지향적인 사고 방식을 이해하기 위해서 세가지 원칙이 중요하다고 주장하는데, 이것들은 모두 객체의 인터페이스에 관련된 것이다.

  • 좀 더 추상적인 인터페이스
  • 최소 인터페이스
  • 인터페이스와 구현 간에 차이가 있다는 점을 인식

이 중에 가장 중요한 원칙은 세번째 이다. 

객체의 외부와 내부를 명확하게 분리하는 것이 중요하다는 것은 책을 읽으며 반복해서 강조당했(?)다.

이때 객체의 외부를 공용 인터페이스라고 부르고 객체의 내부 구조와 작동 방식을 가리키는 것을 구현 이라고 한다.

객체를 구성하지만 공용 인터페이스에 포함되지 않는 모든 것이 구현에 포함된다.

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

 

훌륭한 객체란 구현을 모른 채 인터페이스만 알면 쉽게 상호작용할 수 있는 객체를 의미한다.

우리가 자동차가 어떤 원리로 굴러가는지 모르지만 기어를 넣고, 페달을 밝으면 앞으로 가게 할 수 있는 것과 같다.

기어와 페달이라는 공용 인터페이스의 사용법만 알면 내부 구현을 몰라도 자동차를 사용할 수 있는 것이다.

결론적으로, 객체설계의 핵심은 객체를 두개의 분리된 요소 즉, 외부에 공개되는 인터페이스와 내부에 감춰지는 구현으로 분할해 설계하는 것이다.

 

그렇다면 어떻게 구현내용을 외부로부터 감출수 있을까? 그 행위를 나는 캡슐화라고 알고 있었다.

객체는 상태와 행동을 함께 캡슐화함으로써 자율적인 존재가 될 수 있고, 이것은 다른 객체와의 느슨한 결합으로 이어진다.

캡슐화는 두가지 관점에서 사용되는데, 상태와 행위의 캡슐화와 사적인 비밀의 캡슐화가 그것이다.

1. 상태와 행위의 캡슐화

객체는 스스로 자신의 상태를 관리하며 상태를 변경하고 외부에 응답할 수 있는 행동을 자신의 내부 안에 함께 보관한다.

이렇듯 상태와 행동을 하나의 단위로 묶는 캡슐화를 데이터 캡슐화라고 한다. 

객체는 외부에서 반드시 접근해야만 하는 행위만 골라 공용 인터페이스를 통해 노출하고, 자기 자신의 상태는 스스로 관리함으로서 자율적인 객체가 된다. 과거의 전통적인 개발 방법은 데이터와 프로세스를 엄격하게 구분했지만, 객체지향에서는 이렇게 데이터와 프로세스를 객체라는 하나의 틀 안에 함께 묶어 놓음으로써, 객체의 자율성을 보장한다.

2. 사적인 비밀의 캡슐화

사람들은 자신만의 개인적인 내용이나 공간에 외부인이 함부로 침입할 수 없도록 최대한 방어한다.

객체도 마찬가지다. 객체는 외부의 다른 객체가 자신의 내부 상태를 직접 관찰하거나 제어할 수 없도록 막기 위해 의사소통이 가능한 특별한 경로만 외부에 노출한다. 이것이 바로 공용 인터페이스이다.

한번쯤 인터페이스에 메소드들을 선언해놓고 이것을 구현하는 클래스를 생성하여 메소드들을 정의해본 적이 있을 것이다. (적어도 자바로 개발을 한다면) 

그것이 바로 이 내용인 것 같다는 느낌이 들었다.

객체의 공용 인터페이스는 외부에서 전송 가능한 메시지의 집합이다. 외부 객체는 오직 공용 인터페이스에 정의된 메시지를 통해서만 해당 객체에 접근할 수 있다. 어떠한 행동을 할 수 있다는 것만 공용 인터페이스에 노출하고 개인적인 상태와 행동방식등은 뒤로 숨김으로써 객체는 자율성을 보장받을 수 있게 되는 것이다. 

 

느낀점

참 많은 내용이 담겨있는 장이었던 것 같다.

자주 들어 보았던 캡슐화, 인터페이스, 추상화, 다형성 등 객체지향 프로그래밍의 꽃이라고도 할만한 내용들이 한 장에 담겨있다는 것이 정리 하기에도 부담이 되었던 것이 사실이다.

하나 하나 주제별로 따로 떼어서 공부를 해도 될만큼 이 중요한 내용들이 한 챕터에 담겨있는 이유가 무엇일지 생각해보았다.

나는 결국 이 모든것이 '자율적인 책임'에 의해 파생되는 개념임을 알 수 있었다.

자율적인 책임은 의도를 명확하게 표현함으로써 협력을 단순하고 이해하기 쉽게 만든다.

즉, '무엇'에 포커스를 맞춘 자율적인 책임은 세부적인 사항들을 무시하고 의도를 드러내는 하나의 문장으로 표현함으로써 협력을 단순하게 만드는 것이다.

또한, 자율적인 책임은 공용인터페이스와 구현을 분리시킨다. 

'무엇'에 해당하는 공용 인터페이스와 '어떻게'에 해당하는 구현을 분리시키는 것이다.

'증언해야 하는 책임'과 '목격했던 장면을 떠올려서 시간 순서대로 재구성한 후에 말로써 간결하게 증언해야 하는 책임' 중에 어느 것이 자율적인 책임인지는 자명하다. 위의 한 문장만으로도 자율적인 책임이 공용 인터페이스와 구현을 분리시키는데 중요한 역할을 한다는 것을 알 수 있었다.

자율적인 책임은 그 책임을 수행하기위한 행동 방식을 변경하더라도 외부에 영향을 미치지 않는다.

'증언하라'라는 자율적인 책임에 대하여 '어떻게' 증언할지는 객체가 자율적으로 선택할 수 있다. 그 방법이 바뀐다고 해서 외부의 요청자가 다르게 메시지를 보내야 하는 상황은 오지 않는다.

반면에, '목격했던 장면을 떠올려서 시간 순서대로 재구성한 후에 말로써 간결하게 증언해라' 라는 타율적인 책임에 대해서 객체가 증언을 하는 방식을 말로써 간결하게 하지 않고 글로써 복잡하게 처리하게 된다면 어떻게 될까?

외부의 객체 즉, 요청자 또한 그 요청메시지를 변경해야 하는 복잡한 상황이 벌어지게 된다. 

자율적인 책임은 그 책임이 추상화되어 있기 때문에, 이것에 대해 책임을 질 여러 객체들을 다양하게 선택할 수 있는 유연성을 제공한다.

이것은 너무 자명한 사실이다. 추상화된 요청일 수록 그에 응답할 수 있는 모집단은 많아질 것이다.

 

결론적으로, 이번장에서 느낀것은 자율적인 책임의 중요성이다. 그리고 그 자율적인 책임은 메시지에 따라 달라진다.

메시지는 '무엇'에 해당되는 요청사항 이었다. 애플리케이션을 수행하기 위한 협력관계를 설계함에 있어서 클래스의 상태와 메서드를 먼저 고려할 것이 아니라, 이 메시지를 먼저 고려하는 습관은 반드시 들여야 겠다.