객체지향의 사실과 오해 7장 - 함께 모으기

박진형·2022년 5월 18일
0

함께 모으기

코드와 모델을 밀접하게 연관시키는 것은 코드에 의미를 부여하고 모델을 적절하게 한다. - 에릭 에반스(Eric Evans)

마틴 파울러는 객체지향 설계 안에 존재하는 세가지 상호 연관된 관점에 관해 설명한다.

  • 개념 관점
    • 도메인 안에 존재하는 개념과 개념들 사이의 관계 표현
    • 사용자가 도메인을 바라보는 관점 반영
    • 실제 도메인의 규칙과 제약을 최대한 유사하게 반영하는 것이 핵심
  • 명세 관점
    • 실제 소프트웨어에서의 객체들의 책임에 초점(인터페이스)
    • 명세 관점에서 프로그래머는 객체가 협력을 위해 '무엇'을 할 수 있는가에 초점을 맞춘다.(주문하라, 증언하라, 누가 메시지에 대한 책임을 맡을 것인가?)
    • 인터페이스와 구현을 분리하는 것이 핵심
  • 구현 관점
    • 객체들이 책임을 수행하는 데 필요한 동작하는 코드를 작성하는 것
    • 프로그래머는 객체의 책임을 '어떻게' 수행할 것인가에 초점을 맞추며 인터페이스를 구현하는데 필요한 속성과 메서드를 클래스에 추가.

개념 관점, 명세 관점, 구현 관점의 순서대로 개발하라는 것이 아닌 동일한 클래스를 세가지 다른 방향에서 바라보아야함.

이것들은 클래스를 어떻게 설계해야 하느냐에 대한 중요한 힌트를 암시한다.
-> 반대로 구현된 코드 안에서 세가지 관점을 쉽게 식별할 수 있도록 깔끔하게 분리되어야 한다.

커피 전문점 도메인

커피 주문

커피를 주문하는 과정을 예제로 보여주고 있다.

커피 전문점이라는 세상

커피 전문점에는 메뉴판이 있고, 메뉴판에는 다양한 커피가 있다.
메뉴판은 객체로 볼 수 있고, 메뉴판의 항목들도 역시 객체로 볼 수 있다.
손님은 메뉴판을 보고 바리스타에게 커피를 주문한다(메뉴판에서 메뉴를 선택해 바리스타에게 전달한다.) 손님 역시 객체다.
바리스타는 여러 메뉴들을 자율적으로 제조하는 객체로 볼 수 있고, 커피 역시 메뉴판, 메뉴항목, 바리스타와는 구별되는 자신만의 경계를 가지므로 객체로 볼 수 있다.

-> 객체지향의 관점에서 커피 전문점이라는 도메인은 손님, 메뉴 항목, 메뉴판, 바리스타, 커피 객체로 구성된 작은 세상이다.

객체들 간의 관계

  • 각 객체들은 관계를 맺는다.

    • 손님은 메뉴판을 알아야하므로 관계가 존재한다.
    • 손님은 바리스타에게 주문을 해야하므로 관계가 존재한다. 등
  • 인간의 두뇌는 세상을 이해하기 위해 객체를 직접적으로 다룰 수 있을 만큼 효율적이지 못하다.
    -> 동적인 객체를 정적인 타입으로 추상화하여 복잡성을 낮춰야한다. (타입)
    -> 타입은 분류를 위해 사용, 상태와 무관하게 동일하게 행동할 수 있는 객체를 동일한 타입으로 분류 할 수 있다.
    -> 객체는 특정 타입의 인스턴스로 볼 수 있다.

  • 메뉴 항목은 메뉴판에 포함되는 합성(포함)관계

  • 손님은 메뉴판을 알고 있어야하지만 메뉴판은 손님의 일부가 아니므로 연관 관계

  • 바리스타와 커피도 연관 관계

관계를 맺었다면, 다음 단계는 적절한 객체에게 적절한 책임을 할당하는 것.

💡 참고로 포함 관계, 연관 관계로 구분 짓는 것보다 객체 간에 관계가 존재한다는 사실을 이해하는 것만으로도 충분하다.

설계하고 구현하기

커피를 주문하기 위한 협력 찾기

객체지향 설계의 첫 번째 목표는 훌륭한 객체를 설계하는 것이 아닌 훌륭한 협력을 설계하는 것.
객체가 메시지를 선택하는 것이 아닌 메시지가 객체를 선택하게 해야 한다.
-> 메시지를 선택한 뒤, 수신하기에 적합한 객체를 선택 -> 선택된 객체는 메시지를 처리할 책임을 부여받고 메시지는 공용 인터페이스에 포함된다.

직접 할 수 없는 것은 다른 연관 객체에게 요청을 한다.

  • 토끼책 모임이라는 협력 -> "토끼책 요약 정리 발표하라." -> 발표자의 책임 -> 나는 발표자 타입의 인스턴스
  • 발표자는 질문 할 것이 있는지 묻거나 의견을 요청한다.
    -> 질문을 해달라거나 의견을 달라는 요청에 대해 누군가가 응답할 책임을 가진다.
    -> 발표 내용을 잘 흡수한 발표를 들은 사람들이 책임이 할당될 가장 유력한 후보

즉, 어떤 책임을 수행하기 위한 정보를 가장 많이 가지고 있는 객체가 책임을 지도록 해야한다.

💡 객체 지향 세계에서는 무생물도 능동적이고 자율적일 수도 있다. (메뉴판)

  • 객체지향은 현실세계를 모방,추상화가 아닌 '은유'할 뿐

인터페이스 정리하기

각 객체를 협력이라는 문맥에서 떼어내어 수신 가능한 메시지만 추려해면 인터페이스가 된다.
실제 소프트웨어 구현은 동적인 객체가 아닌 정적인 타입을 이용해 이뤄지므로 객체들을 포괄하는 타입을 정의하고 타입의 인터페이스를 정의해야 한다.

클래스를 이용해 타입을 구현 (클래스와 타입은 엄연히 다르다.)

구현하기

객체에게 메시지를 전송하기 위해서는 참조를 얻어야하고, 이 책에서는 메서드의 인자로 전달 받아 참조 문제를 해결하고 있다. (결과적으로 인터페이스를 변경하는 것)
이후, 메서드의 구현을 채우면 됨. (구현 도중 인터페이스가 변경될 수 있음)

💡 캡슐화를 위해서는 인터페이스를 정하는 단계에서 객체가 어떤 속성을 가지는지, 속성이 어떤 자료구조로 구현됐는지 고려하지 않는게 좋다.
내부 구현에 대한 어떤 가정도 하지 말아야 한다.
책임을 결정한 후에 속성을 결정하라.

💡 인터페이스를 통해 실제로 상호작용을 해보지 않은 채 인터페이스의 모습을 정확하게 예측하는 것은 불가능에 가깝다.
-> 설계를 간단히 끝내고 최대한 빨리 구현에 돌입하라.

코드와 세 가지 관점

코드는 세 가지 관점을 모두 제공해야 한다.

1장에서 설명한 개념 관점, 명세 관점, 구현 관점을 뜻함.

  • 개념 관점

    • 소프트웨어 클래스가 도메인 개념의 특성을 최대한 수용하면 변경을 관리하기 쉽고 유지보수성을 향상시킬 수 있다.
    • 소프트웨어 클래스와 도메인 클래스 사이의 간격이 좁으면 좁을수록 기능 변경을 위한 뒤적거려야하는 코드의 양도 줄어든다.
  • 명세 관점

    • 인터페이스의 수정은 어렵다. 안정적인 인터페이스를 만들기 위해 구현과 관련된 세부 사항을 드러내지 마라.
  • 구현 관점

    • 메서드의 구현과 속성의 변경은 원칙적으로 외부의 객체에게 영향을 미쳐서는 안된다. -> 메소드와 속성의 캡슐화를 의미
    • 외부의 클래스는 협력하는 다른 클래스에 비밀에 의해 영향을 받아선 안된다.

    세 가지 관점이 명확한 코드를 짜라.

도메인 개념을 참조하는 이유

도메인에 대한 지식을 기반으로 객체를 선택하는 것은 코드의 구조와 의미를 쉽게 유추할 수 있도록 한다. -> 유지보수성 향상

인터페이스와 구현을 분리하라

인터페이스가 구현 세부 사항을 노출하면 취약한 설계를 얻을 수 밖에 없다.
-> 객체가 요청 응답을 하는 창구인 인터페이스만알고 구현을 알 필요 없도록 함으로써 영향을 최소화 하자!
마틴 파울러는 명세 관점과 구현 관점을 분리하는 것이 매우 중요하다고 한다.
명세 관점 설계를 주도를 해서 설계 품질을 향상 시켜라!

0개의 댓글