새오의 개발 기록

객체지향의 사실과 오해 본문

독서

객체지향의 사실과 오해

새오: 2023. 1. 21. 11:10

 

 

2023.01.20 - 2023.01.22

 

 

 

리뷰

 

 

객체지향은 클래스가 아니다.

 

 

 

객체지향의 기본 사상을 이해하는데 도움이 되는 책이다. 우리가 흔히 객체지향을 클래스로 코드를 작성하는 거라고 생각하는 경향이 있는데 이러한 오해를 바로잡고, 본래 객체지향의 목표가 무엇인지를 여러 비유를 들어 설명하고 있다. 책의 절반 이상이 객체지향의 개념을 설명하고 있는 것으로 보아 이 책의 목표가 객체지향이 무엇인지를 이해시키겠다는데 있음을 알 수 있다. 이 책의 후속작으로 '오브젝트'에는 코드와 함께 객체지향을 다루고 있다고 하니 이 책을 읽고, 그 책까지 봐야 실질적으로 객체지향을 설계하는데 도움이 될 것 같다. 내가 이 책을 선택하게 되었던 목적은 과제 테스트에서 객체지향을 설계하기 위함이었는데 많은 도움이 되었고 여러 예제와 내용을 통해 역할, 책임, 협력 이라는 관점에서 객체지향에 대해 바라보는 방법을 이해하게 되었다.

 

 

 

핵심 요약

각 챕터마다 중요하다고 생각되는 부분들을 뽑아봤다.

 

 

 

 

1. 협력하는 객체들의 공동체

 

객체지향 패러다임의 핵심은 '자율적인 객체들의 협력'

  • 객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법
  • 자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.
  • 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.
  • 객체는 다른 객체와 협력하기 위해 메시지를 전송하고, 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메서드를 자율적으로 선택한다.
  • 외부의 요청이 무엇인지를 표현하는 메시지와 요청을 처리하기 위한 구체적인 방법인 메서드를 분리하는 것은 객체의 자율성을 높이는 핵심 메커니즘. -> 캡슐화
  • 어떤 클래스가 필요한가가 아니라 어떤 객체들이 어떤 메시지를 주고 받으며 협력하는가가 중요하다. 클래스는 객체들의 협력 관계를 코드로 옮기는 도구에 불과하며 적절한 책임을 수행하는 역할 간의 유연하고 견고한 협력 관계를 구축하는 것이 객체지향의 핵심이다.

 


 

ex) 커피를 주문하기 위해 협력하는 사람들

 

  • 협력: 커피주문
  • 객체: 손님, 캐시어, 바리스타
  • 메시지: 커피 제조를 요청하는 말, 주문 내역이 기록된 컵
  • 메서드: 커피를 제조하는 구체적인 방법
  • 역할: 손님, 캐시어, 바리스타
  • 책임
    • 손님: 커피를 주문한다.
    • 캐시어: 손님으로부터 주문을 받는다.
    • 바리스타: 주문된 커피를 제조한다.

 

 

 

 

2. 이상한 나라의 객체

 

  • 객체지향 패러다임의 목적은 현실 세계를 모방하는 것이 아니라 현실 세계를 기반으로 새로운 세계(소프트웨어 세계)를 창조하는 것
  • 객체는 상태를 가지며 상태는 변경 가능하다.
  • 객체의 상태를 변경시키는 것은 객체의 행동이다.
    • 행동의 결과는 상태에 의존적이며 상태를 이용해 서술할 수 있다.
    • 행동의 순서가 실행 결과에 영향을 미친다.
  • 객체는 어떤 상태에 있더라도 유일하게 식별 가능하다.

 


 

ex) 앨리스 객체

 

앨리스는 정원의 문을 통과하기에 적당한 상태로 자신의 키를 계속해서 변화시킬 수 있으며 음료를 들고 있다.

그러므로 상태인 앨리스의 키와 위치, 음료의 양은 변화할 수 있다.

 

  1. 앨리스 객체
    • 상태: 키 = 130cm, 위치 = '통로'
  2. 음료 객체
    1. 상태: 양 = 0.5L

 

  • 앨리스는 상태를 가지며 상태는 변경 가능하다.
    앨리스의 상태를 변경시키는 것은 앨리스의 행동이다.
    앨리스는 어떤 상태에 있더라도 유일하게 식별 가능하다.
 
 

 

 

 

 

3. 타입과 추상화

 

추상화

  • 특정 목적을 달성하기 위해 복잡한 부분을 감추고 본질적인 부분만 드러내는 행위
  • 주로 객체들을 개념 안에 분류하는 방식으로 추상화를 실현
  • 두 가지 차원의 추상화
    • 첫번째 차원은 구체적인 사물들 간의 공통점은 취하고 차이점은 버리는 일반화를 통해 단순하게 만드는 것
    • 두번째 차원은 중요한 부분을 강조하기 위해 불필요한 세부 사항을 제거함으로써 단순하게 만드는 것

 


 

ex) 앨리스가 트럼프를 바라보고 있는 상황

 

  • 앨리스는 수 많은 트럼프를 하나의 트럼프로 바라보게 되는데, 이는 수 많은 트럼프를 하나의 트럼프로 추상화한 것
  • 객체에 어떤 개념을 적용하는 것이 가능해서 개념 그룹의 일원이 될 때 객체를 그 개념의 인스턴스(instance)라고 한다.
  • 객체란 특정한 개념을 적용할 수 있는 구체적인 사물을 의미한다. 개념이 객체에 적용됐을 때 객체를 개념의 인스턴스라고 한다.
  • 객체에서 중요한 것은 객체의 행동이다. 객체가 협력을 위해 어떤 책임을 지녀야 하는지를 결정하는 것이 객체지향 설계의 핵심이다.
 

 

4. 역할, 책임, 협력

 

  • 객체지향에 갓 입문한 사람들의 가장 흔한 실수는 협력이라는 문맥을 고려하지 않은 채 객체가 가져야 할 상태와 행동부터 고민하기 시작한다는 것이다. 중요한 것은 개별 객체가 아니라 객체들 사이에 이뤄지는 협력이다.
  • 동일 역할을 수행할 수 있다는 것은 해당 객체들이 협력 내에서 동일한 책임의 집합을 수행할 수 있다는 것을 의미한다.
  • 역할의 개념을 사용하면 유사한 협력을 추상화해서 인지 과부하를 줄일 수 있고, 협력이 좀 더 유연해지며 다양한 객체들이 동일한 협력에 참여할 수 있기 때문에 재사용성이 높아진다.
  • 역할은 객체지향 설계의 단순성, 유연성, 재사용성을 뒷받침하는 핵심 개념이다.

 

객체지향 설계 기법

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

    • 협력에 필요한 책임들을 식별하고 적합한 객체에게 책임을 할당하는 방식으로 애플리케이션을 설계한다.

2. 디자인 패턴(Design Pattern)

    • 전문가들이 반복적으로 사용하는 해결 방법을 정의해 놓은 설계 템플릿의 모음.

    • 역할, 책임, 협력이 디자인 패턴 안에 이미 존재한다.

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

    • 테스트를 먼저 작성하고 테스트를 통과하는 구체적인 코드를 추가하면서 애플리케이션을 완성해가는 방식

    • 테스트가 아닌 설계를 위한 기법

    • 객체가 이미 존재한다고 가정하고 객체에게 어떤 메시지를 전송할 것인지에 관해 먼저 생각하라고 충고한다.

        • 이러한 충고는 역할, 책임, 협력의 관점에서 객체를 바라보지 않을 경우 무의미하다.

 

 

 

5. 책임과 메시지

 

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

  • 하나의 객체는 메시지를 전송함으로써 다른 객체에 접근하며 객체가 다른 객체에게 접근할 수 있는 유일한 방법이다.
  • 송신자는 메시지 전송을 통해서만 다른 객체의 책임을 요청할 수 있고, 수신자는 오직 메시지 수신을 통해서만 자신의 책임을 수행할 수 있다. 메시지의 모양이 객체가 수행할 책임의 모양을 결정한다.
  • 객체가 제공하는 메시지는 외부의 다른 객체가 볼 수 있는 공개된 영역에 속한다.(public)
  • 메시지를 처리하기 위해 책임을 수행하는 방법은 외부의 다른 객체가 볼 수 없는 객체 자신의 사적인 영역에 속한다(private)
  • 메시지를 수신한 객체가 실행 시간에 메서드를 선택할 수 있다는 사실은 다른 프로그래밍 언어와 객체지향 프로그래밍 언어를 구분 짓는 핵심적인 특징 중 하나다.

 다형성

    • 서로 다른 유형의 객체가 동일한 메시지에 대해 서로 다르게 반응하는 것

    • 더 구체적으로는, 서로 다른 타입에 속하는 객체들이 동일한 메시지를 수신할 경우

      서로 다른 메서드를 이용해 메시지를 처리할 수 있는 메커니즘을 가리킨다.

    • 송신자와 수신자 간의 객체 타입에 대한 결합도를 메시지에 대한 결합도로 낮춤으로써 달성된다.

        • 다형성을 통해 객체지향이 유연하고 확장 가능하고 재사용성이 높아짐 

 

 

메시지를 따라라

  • 훌륭한 객체지향 설계는 어떤 객체가 어떤 메시지를 전송할 수 있는가와 어떤 객체가 어떤 메시지를 이해할 수 있는가를 중심으로 객체 사이의 협력 관계를 구성하는 것이다. 

 

 

객체 인터페이스

  • 일반적으로 인터페이스란 어떤 두 사물이 마주치는 경계 지점에서 서로 상호작용할 수 있게 이어주는 방법이나 장치를 의미
  • 인터페이스의 특징
    • 인터페이스의 사용법을 익히기만 하면 내부 구조나 동작 방식을 몰라도 쉽게 조작하거나 의사를 전달할 수 있다
    • 인터페이스 자체는 변경하지 않고, 단순히 내부 구성이나 작동 방식을 변경하는 것은 인터페이스 사용자에게 아무런 영향이 없다.
    • 대상이 변경되더라도 동일한 인터페이스를 제공하기만 하면 아무런 문제없이 상호작용 할 수 있다.

인터페이스와 구현의 분리 - 객체 관점에서 생각하는 방법

    • 좀 더 추상적인 인터페이스

    • 최소 인터페이스

    • 인터페이스와 구현 간에 차이가 있다는 점을 인식

 

 

인터페이스와 구현의 분리 원칙

  • 훌륭한 객체란 구현을 모른 채 인터페이스만 알면 쉽게 상호작용할 수 있는 객체를 의미한다.
  • 객체 설계의 핵심은 객체를 두 개의 분리된 요소로 분할해 설계하는 것
    • 외부에 공개되는 인터페이스와 내부에 감춰지는 구현
    • 소프트웨어는 항상 변하기 때문에 인터페이스와 구현의 분리 원칙은 중요하다!

 

 

 

 

6. 객체 지도

 

  • 유일하게 변하지 않는 것은 모든 것이 변한다는 사실 뿐이다.
  • 자주 변경되는 기능이 아니라 안정적인 구조를 따라 역할, 책임, 협력을 구성하라

유스케이스

 

    • 사용자의 목표를 달성하기 위해 사용자와 시스템 간에 이뤄지는 상호작용의 흐름을 텍스트로 정리한 것

    • 유스케이스는 사용자와 시스템 간의 상호작용을 보여주는 '텍스트'다.

 

 

 

 

7. 함께 모으기

 

  • 인터페이스와 구현을 분리하라
  • 클래스를 명세 관점과 구현 관점으로 구분할 수 있어야 한다. 
  • 클래스는 개념, 명세, 구현 관점을 모두 수용할 수 있도록 설계해야 한다.

 

개념관점

  • 설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현한다.
  • 이 관점은 사용자가 도메인을 바라보는 관점을 반영한다.
  • 실제 도메인의 규칙과 제약을 최대한 유사하게 반영하는 것이 핵심이다.

 

 

명세관점

  • 사용자의 영역인 도메인을 벗어나 개발자의 영역인 소프트웨어로 초점이 옮겨진다. 실제 소프트웨어 속의 객체들의 책임에 초점을 맞추게 된다.
  • 명세 관점에서 프로그래머는 객체가 협력을 위해 '무엇'을 할 수 있는가에 초점을 맞춘다.
  • 구현과 인터페이스를 분리하는것이 가장 중요하다.

 

 

구현관점

  • 실제 작업을 수행하는 코드에 초점을 맞춘 관점이다. 즉, 객체에 주어진 책임을 '어떻게' 수행할 것인가에 초점을 맞추며 인터페이스를 구현하는 데 필요한 속성과 메서드들을 클래스에 추가한다.

 

 

 

 

 

 

참고: 객체지향의 사실과 오해, https://zzang9ha.tistory.com/297