독후감

[독후감] 객체지향의 사실과 오해

Choony 2023. 9. 10. 11:37

객체지향이란 무엇인가?

 

배워야 할게 산더미인 나에게 대답하기 너무 어려운 질문이다.

지금 글을 쓰고 있는 이 순간에도 객체지향이란 무엇일까 생각하며 멍 때리는 중이다.

 

그나마 이 책을 한번 읽고나서 배운 거에 대해 적어보고자 한다.

 

잘 모르겠는 질문에 대한 답변대신 일단 객체지향이라고 하면 떠오르는 키워드를 나열해 보자

캡슐화, 다형성, 클래스, 상속, 추상화,,,,

 

학부 객체지향 수업 때 지겹도록 들은 말이다. 그리고 항상 따라오는 설명을 적어보겠다.

  • 붕어빵틀은 클래스이며 붕어빵은 객체(인스턴스)이다.
  • 사과, 바나나, 메론은 '과일'로 추상화할 수 있다.

또는 '객체지향이란 현실세계의 모방이다'도 심심찮게 볼 수 있다.

서론이 길어지는 거 같으니 하고 싶은 말부터 하겠다.

 

객체지향은 현실세계의 모방이 아니다. 오히려 새로운 세계를 창조하는 것이다.

 

그렇다면 왜 '객체지향은 현실세계의 모방이다'와 같은 설명을 하는 걸까?

이 설명은 객체지향적인 프로그램을 설계하고 구현하는 실무적인 관점에서는 부적합하지만 객체지향의 기본 사상과 개념을 이해하기에 좋기 때문이다.

 

하지만 이 책을 읽고 다룰 내용은 아닌 거 같으므로 이제 '모방'말고 '새로운 세계의 창조'에 집중하여 글을 써보겠다.

 

현실세계와 새로운 세계(객체 세계)의 가장 큰 차이점은 새로운 세계의 모든 객체들은 각각이 자신의 상태를 관리하는 자율적인 존재이다.

 

한 사람이 커피를 마시는 상황을 예로 들어보겠다.

현실세계에서 커피를 마시는 건 사람 객체가 커피를 마시면서 컵 속에 들어있는 커피 객체의 양을 줄어들게 한다. 즉, 사람 객체가 커피 객체의 상태를 변화시키는 것이다.

 

하지만 새로운 세계에서는 사람 객체와 커피 객체는 모두 자율적인 존재다. 사람 객체가 커피를 마신다고 해서 커피 객체의 상태(양)가 자동으로 변하는 게 아니라 커피 객체가 스스로 자신의 양을 줄여야 상태가 변하는 것이다. 

 

객체는 자율적인 존재이다. 객체 세계에서 객체는 다른 객체의 상태에 직접적으로 접근할 수도 변경할 수도 없다. 자율적인 객체는 스스로 자신의 상태를 책임져야 한다.

이렇듯 객체지향의 중요한 점은 '객체지향의 세계에서는 모든 객체가 자율적이다'라는 것이며 이를 받아들이는 것이다.

 

역할, 책임, 메시지

이 책에서 자주 등장하는 단어들이다. 앞으로의 글을 위해서 정리해보고자 한다.

역할

  • 역할은 관련성 높은 책임의 집합이다. 객체의 역할은 사람의 역할과 유사하게 다음과 같은 특징을 가진다.
    • 여러 객체가 동일한 역할을 수행할 수 있다.
    • 역할은 대체 가능성을 의미한다.
    • 각 객체는 책임을 수행하는 방법을 자율적으로 선택할 수 있다.
    • 하나의 객체가 동시에 여러 역할을 수행할 수 있다.
  • 역할은 협력 내에서 다른 객체로 대체할 수 있음을 나타내는 일종의 표식이다. 협력 안에서 역할은 "이 자리는 해당 역할을 수행할 수 있는 어떤 객체라도 대신할 수 있습니다"라고 말하는 것과 같다.

책임

  • 책임은 객체의 외부에 제공해 줄 수 있는 정보(아는 것의 측면)와 외부에 제공해 줄 수 있는 서비스(하는 것의 측면)의 목록이다. 따라서 책임은 객체의 공용 인터페이스를 구성한다.
  • 객체의 책임은 크게 '하는 것'과 '아는 것'의 두 가지 범주로 분류된다.
    • 하는 것
      • 객체를 생성하거나 계산을 하는 등의 스스로 하는 것
      • 다른 객체의 행동을 시작시키는 것
      • 다른 객체의 활동을 제어하고 조절하는 것
    • 아는 것
      • 개인적인 정보에 관해 아는 것
      • 관련된 객체에 대해 아는 것
      • 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
  • 객체가 어떤 행동을 하는 유일한 이유는 다른 객체로부터 요청을 수신했기 때문이다. 요청을 처리하기 위해 객체가 수행하는 행동을 책임이라고 한다. 따라서 자율적인 객체란 스스로의 의지와 판단에 따라 각자 맡은 책임을 수행하는 객체를 의미한다.

메시지

  • 객체가 다른 객체에게 주어진 책임을 수행하도록 요청을 보내는 것을 메시지 전송이라고 한다.
  • 객체가 다른 객체에게 접근할 수 있는 유일한 방법은 요청을 전송하는 것이며 이때 요청이 메시지이다.
  • 메시지는 객체로 하여금 자신의 책임, 즉 행동을 수행하게 만드는 유일한 방법이다.
  • 객체가 유일하게 이해할 수 있는 의사소통 수단은 메시지뿐이며 객체는 메시지를 처리하기 위한 방법을 자율적으로 선택할 수 있으며 이것은 위에서 말했던 자율적인 객체이다. 
  • 외부의 객체는 메시지에 관해서만 볼 수 있고 객체 내부는 볼 수 없기 때문에 자연스럽게 객체의 외부와 내부가 분리된다. 이는 캡슐화와 관련이 깊다.

이 문단에서 말하고 싶은 내용은 이것이다.

메시지를 처리할 수 있다는 것은 객체가 해당 메시지에 해당하는 행동을 수행해야 할 책임이 있다는 것을 의미한다. 따라서 메시지의 개념은 책임의 개념과 연결된다. 송신자는 메시지 전송을 통해서만 다른 객체의 책임을 요청할 수 있고, 수신자는 오직 메시지 수신을 통해서만 자신의 책임을 수행할 수 있다. 따라서 객체가 수신할 수 있는 메시지의 모양이 객체가 수행할 책임의 모양을 결정한다.

 

협력

첫 문단에서는 '각 객체는 모두 자율적이다' 이 점을 강조하였다.

이 문단에서 강조할 것은 '객체의 협력'이다.

객체지향의 기본 개념은 책임을 수행하는 자율적인 객체들의 협력을 통해 애플리케이션을 구축하는 것이다.
객체지향의 세계에서 객체들이 서로 협력하기 위해 사용할 수 있는 유일한 방법은 메시지를 전송하는 것이다.
객체지향 설계의 첫 번째 목표는 훌륭한 객체를 설계하는 것이 아니라 훌륭한 협력을 설계하는 것이다.

문장에서도 보이듯 객체지향에 있어 중요한 것은 자율적인 객체들의 협력이다.

 

이렇듯 객체지향 프로그램을 설계, 구현할 때 중요한 것은 객체와 객체들의 협력을 중심으로 생각하는 것이다.

하지만 나 또한 그랬고 많은 사람들은 객체지향 애플리케이션을 클래스의 집합으로 생각하는 착오를 범한다.

 

물론 개발자들에게 클래스는 실제로 볼 수 있고 수정할 수 있는 구체적인 존재이지만 클래스는 단지 동적인 객체들의 특성과 행위를 정적인 텍스트로 표현하기 위해 사용할 수 있는 추상화 도구일 뿐이라는 것을 깨달아야 한다. 중요한 것은 클래스가 아니라 객체임을 깨닫자. 

 

다시 돌아와 객체지향 설계에 대해서 말하고자 한다.

 

위에서 객체 간 협력의 유일한 방법은 메시지라고 하였다.

때문에 객체지향 설계에 있어서 메시지는 매우 중요한 키워드이다. 훌륭한 객체지향 설계는 어떤 객체가 어떤 메시지를 전송할 수 있는가와 어떤 객체가 어떤 메시지를 이해할 수 있는가를 중심으로 협력관계를 구성하는 것이다. 

 

객체가 메시지를 선택하는 것이 아닌 메시지가 객체를 선택하게 해야 한다. 이를 위해선 메시지를 중심으로 협력을 설계해야 한다.

협력이라는 문맥 안에서 객체의 책임을 결정하는 것은 메시지이며, 책임이 먼저 오고 객체가 책임을 따른 다는 사실을 잊지 말자. 결과적으로 시스템이 수행해야 하는 전체 행위는 협력하는 객체들의 책임으로 분배된다. 

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

 

훌륭한 객체지향 설계를 하기 위해 다양한 객체지향 설계 기법이 존재하는데, 책에서 다루는 세 가지 설계 기법에 대해 간단하게 설명해 보겠다.

 

책임-주도 설계 (Responsibility-Driven Design)

  • 객체지향 설계란 애플리케이션의 기능을 구현하기 위해 협력 관계를 고안하고, 협력에 필요한 역할과 책임을 식별한 후 이를 수행할 수 있는 적절한 객체를 식별해 나가는 과정이다. 객체지향 설계의 핵심은 올바른 책임을 올바른 객체에게 할당하는 것이다.
  • 책임-주도 설계에서는 시스템의 책임을 객체의 책임으로 변환하고, 각 객체가 책임을 수행하는 중에 필요한 정보나 서비스를 제공해 줄 협력자를 찾아 해당 협력자에게 책임을 할당하는 순차적인 방식으로 객체들의 협력 공동체를 구축한다. 책임-주도 설계는 개별적인 객체의 상태가 아니라 객체의 책임과 상호작용에 집중한다.
    • 이는 객체지향 애플리케이션의 중심 사상인 연쇄적으로 메시지를 전송하고 수신하는 객체들 사이의 협력관계를 기반으로 사용자에게 유용한 기능을 제공한다는 것과 관련이 깊다고 생각하였다.

디자인 패턴(Design Pattern)

  • 디자인 패턴은 책임-주도 설계의 결과를 표현한다. 패턴은 모범이 되는 설계이다.
  • 패턴은 반복해서 일어나는 특정한 상황에서 어떤 설계가 왜 더 효과적인지에 대한 이유를 설명한다.
  • 특정 상황에 적용 가능한 디자인 패턴을 잘 알고 있다면 책임-주도 설계의 절차를 순차적으로 따르지 않고도 시스템 안에 구현할 객체들의 역할과 책임, 협력 관계를 빠르고 손쉽게 포착할 수 있다.

테스트-주도 개발(Test-Driven Development)

  • 테스트-주도 개발의 기본 흐름은 실패하는 테스트를 작성하고, 테스트를 통과하는 가장 간단한 코드를 작성한 후, 리팩터링을 통해 중복을 제거하는 것이다.
  • 테스트-주도 개발은 객체가 이미 존재한다고 가정하고 객체에게 어떤 메시지를 전송할 것인가에 대해 먼저 생각하라고 충고한다. 그러나 이 같은 종류의 충고는 역할, 책임, 협력의 관점에서 객체를 바라보지 않을 경우 무의미하다.
  • 테스트-주도 개발은 테스트를 작성하는 것이 아니라 책임을 수행할 객체 또는 클라이언트가 기대하는 객체의 역할이 메시지를 수신할 때 어떤 결과를 반환하고 그 과정에서 어떤 객체와 협력할 것인지에 대한 기대를 코드의 형태로 작성하는 것이다.
  • 테스트를 작성하기 위해 객체의 메서드를 호출하고 반환값을 검증하는 것은 순간적으로 객체가 수행해야 하는 책임에 관해 생각한 것이다.

 

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

이 과정을 What/Who 사이클이라고 한다. 이 말은 위에서 말했던 '메시지가 협력에 필요한 객체를 발견해야 한다'에 추가 설명이 될 거 같다.

 

글을 쓰다 보니 나도 아직 완전히 이해 못 한 개념들을 나열한 느낌이 든다.

객체지향 설계 기법은 앞으로 나의 개발인생에 있어서 게임하듯 헤쳐나갈 과제이므로 현재는 이 정도 쓰고 마무리해봐야겠다.

 

대신 '메시지가 협력에 필요한 객체를 발견해야 한다'에 간단한 예시를 들며 글을 마쳐야겠다.

 

커피숍 상황을 다시 가져와야겠다. 손님이 커피를 주문하는 과정을 예시로 들것이다

그전에 도메인 설계를 먼저 해보자.

 

커피숍에서 꼭 필요한 도메인들을 생각해 보자면 
손님, 바리스타, 메뉴판(안에 메뉴판 항목들)이 있을 것이다.

 

위에서 객체지향의 첫 번째 목표는 훌륭한 협력을 설계하는 것이라고 하였다.

여기선 '커피 주문하기'를 협력으로 두고 얘기할 것이다.

 

이 협력에서 필요한 메시지는 첫 번째로 '커피를 주문하라'라는 메시지일 것이다.

메시지를 골랐으니 가장 적절한 객체를 생각해 보자. '커피를 주문하라'라는 메시지를 수신할 가장 적절한 객체는 손님 객체일 것이다.

우리는 벌써 '메시지가 협력에 필요한 객체를 발견해야 한다'라는 규칙을 따랐다.

'커피를 주문하라'라는 메시지를 만들었으며 후에 적절한 객체를 손님 객체로 선택하였다. 

손님 객체는 뭘 해야하지? 바리스타 객체는 뭘 해야하지? 와 같은 독립 객체에 대한 고민은 나쁜 협력으로 갈 수 있으며 자율적인 객체를 해칠 수 있는 위험한 행동이다.

 

'커피 주문' 협력에는 손님, 메뉴판, 바리스타 사이의 협력이 있을 것이다. 첫 번째 협력을 다뤘으니 나머지 협력도 잘 해낼 수 있을 거라 믿고 이 글을 마무리하겠다.

시스템의 책임을 객체들의 책임으로 분배한다는 내용에 대해서 쉽게 이해할 수 있는 좋은 예제라고 생각하니 꼭 해보길 바란다.

 

 

ps. 앞으론 이 책의 다음 책이라 볼 수 있는 '오브젝트'에 대해 읽을 예정이다. 객체지향의 이해는 멀고도 험한 것 같다. 화이팅🫡