반응형

Chapter07 함께 모으기

코드와 모델을 밀접하게 연관 시키는 것은 코드에 의미를 부여하고 모델을 적절하게 한다.

  • 에릭 에반스 -

마틴 파울러의 객체 지향 설계 세가지 관점

UML Distilled 2판을 통해서 객체 지향 설계에 존재하는 세가지 관점에 관해 설명한다.

  • 개념 관점(Conceptual Perspective)
  • 명세 관점(Specification Perspective)
  • 구현 관점(Implementation Perspective)

개념 관점

설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현한다. 소프트웨어는 도메인에 존재하는 문제를 해결하기 위해 개발되는데, 이 관점이 사용자가 도메인을 바라보는 관점을 반영한다. 때문에 사용자에 필요한 소프트웨어를 만들기 위해서는 실제 도메인의 규칙과 제약을 최대한 유사하게 반영해야 한다.

명세 관점

사용자의 영역인 도메인을 벗어나 개발자의 영역인 소프트웨어로 초점이 옮겨진다.
명세 관점은 도메인의 개념이 아니라 실제 소프트웨어 안의 객체들의 책임에 초점을 맞춘다. 즉 객체의 인터페이스를 바라보게 된다.

명세 관점은 즉, 프로그래머가 객체가 협력을 위해 ‘무엇’을 할 수 있는가에 초점을 맞춘다.

인터페이스와 구현을 분리하는 것이 훌륭한 객체 지향 설계를 낳는 가장 기본적인 원칙이다.
이 것이 설계의 관점에서는 명세 관점과 구현 관점을 분리하는 것이다.

구현 관점

프로그래머인 우리에게 가장 익숙한 관점으로, 실제 작업을 수행하는 코드와 연관되어 있다.
객체의 책임을 ‘어떻게’ 수행할 것인가 에 초점을 맞추며 인터페이스를 구현하는 데 필요한 속성과 메서드를 클래스에 추가한다.

세 가지 관점의 융합설계

이 세 관점을 통해 개념 → 명세 → 구현 관점 순으로 소프트웨어를 개발하는 것처럼 오해할 수 있는데, 동일한 클래스를 세 가지 다른 방향에서 바라보며 설계하는 것을 의미한다.

클래스는 세 가지 관점이라는 안경을 통해 설계와 관련된 다양한 측면을 드러낼 수 있다. 클래스가 은유하는 개념은 도메인, 공용 인터페이스는 명세, 속성과 메서드는 구현 등과 같이 각각 관점을 반영한다.

이 관점에 맞게 세 가지 관점을 쉽게 식별할 수 있도록 깔끔하게 분리하는 것이 좋은 객체지향 설계를 했다고 볼 수 있다.

커피 전문점 도메인

커피 전문점 예제를 통해 어떤 형식으로 설계하고 구현해나갈지 살펴보기

커피 전문점 세상

커피 전문점을 구성하는 요소들에 관해 생각해보자. 객체 지향 패러다임의 가장 중요한 도구는 객체이므로 커피 전문점을 객체들로 구성된 작은 세상으로 쪼개본다면, 메뉴판, 메뉴판에 있는 커피메뉴가 있을 때 메뉴판 안에 네 개의 메뉴 항목으로 구성돼 있는데 메뉴 항목들 역시 객체로 볼 수 있다. 따라서 메뉴판은 네 개의 메뉴 항목 객체들을 포함하는 객체라고 볼 수 있다. 이를 토대로 도메인 모델을 만들어보자

도메인 모델 설계 과정

image

객체들 간의 관계를 상태와 무관하게 동일한 행동을 하는 객체를 동일한 타입으로 분류

손님 = {손님}
바리스타 = {바리스타}
메뉴판 = {메뉴판}
메뉴 항목 = {카라멜 마키아또, 아메리카노, 카푸치노, 에스프레소}

타입 간의 관계 확인

메뉴판 → 메뉴 항목 은 하나의 단위로 움직이기 때문에 메뉴 항목은 메뉴판 객체에 포함(Containment) 되어있다. 합성(Composition) 으로도 부른다.

바리스타 → 커피

!바리스타와 메뉴판이나 메뉴 항목과 포함 관계가 아닌 것을 유의해야 한다.

image

최종적으로 이렇게 관계 형성까지 마무리가 되면 이것이 도메인 모델이 된다. ⇒ 개념 관점 설계

주의해야 할 점으로는, 실제 도메인 모델을 작성하는 단계에서 어떤 관계가 포함이고 어떤 관계가 연관인지 보다 어떤 타입이 도메인을 구성하는지 파악하는 것이 제대로 도메인을 이해하는 것이다.

설계하고 구현하기

협력 찾기(명세 관점 설계)

협력을 잘 설계하는 것은 메시지가 객체를 선택해야한다.

  1. 시스템💬 : 커피 주문(아메리카노) → 손님
  2. 손님💬 : 메뉴 항목을 찾아라(아메리카노) → 메뉴판
  3. 메뉴판☕(아메리카노) : → 손님 (메시지x)
  4. 손님💬 : 커피를 제조해라(아메리카노) → 바리스타
  5. 바리스타💬 : 커피 생성하라(아메리카노) → 커피
  6. 커피☕(아메리카노) : 생성완료 → 바리스타 (메시지x)
  7. 커피☕(아메리카노) : 커피 제조완료 → 손님(메시지x)

형태로 메시지가 전달되는 객체의 인터페이스가 만들어진다. 객체가 수신한 메시지가 객체의 인터페이스를 결정하는데, 이렇게 협력을 통해 각 객체가 메시지를 주고받는 것을 객체의 인터페이스 라고 한다. 객체의 인터페이스를 통해 명세 관점 설계를 한다.

이를 토대로 만들어진 인터페이스

class Customer {
        public void order(String menuName){}
}

class MenuItem {
}

class Menu {
        public MenyItem choose(String name){}
}

class Barista {
        public Coffee makeCoffee(MenuItem menuItem) {}
}

class Coffee {
        public Coffee(MenuItem menuItem) {}
}

구현하기

각 클래스의 인터페이스를 식별했으므로 오퍼레이션을 수행하는 방법을 메서드로 구현하는 것을 예시로 들자면

class Customer{
        public void order(String menuName, Menu menu, Barista barista){
                MenuItem menuItem = menu.choose(menuName);
                Coffee coffee = barista.makeCoffee(menuItem);
                ...
        }
}

다음과 같이 실제 메시지를 객체전달을 하기 위해 메시지에 객체를 넣어서 참조 문제를 해결하기도 하고, 내부 구현 또한 각 객체에 맞게 구현한다.

결국 구현은 설계와 다를 수밖에 없다. 이를 생각해서 협력을 구상할 때 너무 오랜 시간 쏟는 것이 아니라 코드를 구현 후에 설계가 구현 가능한지, 이상이 없는지 등 확인해봐야 할 것이다.

코드와 세 가지 관점

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

  • 개념 관점 : Customer, Menu, MenuItem, Barista, Coffee 클래스
  • 명세 관점 : public 메서드(메시지, 협력이 가능한 공용 인터페이스)
  • 구현 관점 : 클래스 내부의 메서드나 속성 등

개념 관점을 잘 지켜야 직관적으로 어느 로직이 어느 곳에 들어갈지 파악이 쉽고, 명세 관점을 잘 지켜야 변화에 안정적인 설계가 될 수 있고, 구현 관점을 잘 지켜야 외부의 클래스가 알지 못하게 각 객체 내부에서 요구사항이 변경되어도 잘 돌아갈 수 있는 객체 지향 설계가 될 수 있을 것이다.

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

도메인 개념을 잘 지켜야 도메인에 대한 지식을 기반으로 코드의 구조와 의미를 쉽게 유추할 수 있기에 유지보수성에 큰 영향을 끼친다.

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

명세 관점과 구현 관점이 섞이면 좋은 코드가 아니다.

명세 관점 → 인터페이스

구현 관점 → 내부 구현

명세 관점인 인터페이스를 통해 내부 구현을 숨기고, 구현 관점을 통해 구현하라.

반응형

+ Recent posts