Skip to content

객체의 자율성 vs 객체간의 의존성

좀 더 개선하기

지난 글에서는 Theater가 티켓의 구매 및 판매 처리에 대한 책임을 AudienceTicketSeller가 나누어 갖도록 buy와 sellTo 메소드로 이동시켰다.

그 결과로 Audience에는 Ticket의 의존성이 추가가 되었고, TicketSeller에도 Ticket 의존성이 추가되었다. 이 의존성은 과연 옳은 것일까?

Bag과 TicketOffice 에게도 자율성을 부여해볼까?

질문에 대해 답하기 전에 Bag과 TicketOffice를 생각해보자. 실제 세상은 Bag과 TicketOffice는 수동적인 존재이다. 가방은 자기 스스로 열 수 없고, 어떤 물품이 들어있는지도 소유자가 직접 확인해야한다. TicketOffice도 Ticket이 있는지 확인하려면 TicketSeller를 통해 확인해야 한다.

하지만, <<오브젝트>>에서는 실제 세계의 수동적인 존재도 적극적인 존재로 변화시킬 수 있다고 한다. 이를 의인화(anthropomorphism)라고 말한다.

비록 현실에서는 수동적인 존재라고 하더라도 일단 객체지향의 세계에 들어오면 모든 것이 능동적이고 자율적인 존재로 바뀐다. 레베카 워프스브록(Rebecca Wirfs-Brock)은 이처럼 능동적이고 자율적인 존재로 소프트웨어 객체를 설계하는 원칙을 가리켜 의인화 라고 부른다. -오브젝트 33p-

의인화를 통해 Bag과 TicketOffice를 자율적인 존재로 만들어 보자. Bag에 hold 메소드를 부여하여 티켓이 있는지 확인하는 코드를 Bag에 책임을 전가하자

의인화1
public class Audience {
    // ...
    public Long buy(Ticket ticket) {
        return bag.hold(ticket);
    }
}

public class Bag {
    // ...
    public Long hold(Ticket ticket) {
        if(hasInvitation()) {
            setTicket(ticket);
            return 0L;
          } else {
            setTicket(ticket);
            minusAmount(ticket.getFee());
            return ticket.getFee();
        }
    }
    private boolean hasInvitation();
    private void setTicket();
    private void minuAmount();
}

hold 메소드를 통해 Audience는 훨씬 가벼워졌으며 Bag이 가지고 있는 메소드를 이용해 티켓을 알아서 구매하도록 변경이 되었다. 또한, Bag에 public 으로 제공되었던 메소드를 private로 옮겨져 이전보다 캡슐화가 잘되었음을 확인할 수 있다 즉, Bag을 사용하기 위해서는 hold만 알도록 변경이 되었다.

TicketOffice도 마찬가지로 ticket을 판매하고 난 수익을 자기가 책임지도록 sellTicketTo() 메소드를 활용하여 의인화시켜보자.

의인화2
public class TicketSeller {
    // ...
    public void SellTo(Audience audience) {
        ticketOffice.sellTicketTo(audience);
    }
}

public class TicketOffice {
    // ...
    public void sellTicketTo(Audience audience) {
        plusAmount(audience.buy(getTicket()));
    }

    private void plusAmount(Long amount);
    private Ticket getTicket();
}

의인화 결과 TicketSeller의 책임이 좀더 줄어들고, TicketOffice의 public 메소드였던 plusAmount와 getTicket이 sellTicketTo 메소드가 추가되면서 private로 변경되었다. 이는 외부의 관점에서 TicketOffice를 사용하기 위해 알아야 하는 메소드가 sellTicketTo 하나로 줄어드는 효과가 있다.

의인화한 결과를 클래스 다이어그램을 그리면 아래와 같은 결과가 나타나게 되고, TicketOffice에 Audience의존성이 새롭게 추가된 것을 볼 수 있다. 이는 적절한가? 적절하지 않은가?

Image title

그림 1 - 클래스 다이어그램 1

객체의 의존성 vs 자율성

결론부터 이야기하지면, 이 부분은 문제를 해결하고자 하는 비즈니스 성격에 따라 적절한 트레이드 오프가 이루어져야 한다고 작가는 설명한다.

현재로서는 Audience에 대한 결합도와 TicketOffice의 자율성 모두를 만족시키는 방법이 잘 떠오르지 않는다. 트레이드오프 시점이 왔다. 어떤 것을 우선시 해야 하는가? 토론 끝에 개발팀은 TicketOffice의 자율성보다는 Audience에 대한 결합도를 낮추는 것이 더 중요하다는 결론에 도달했다.

설계는 균형의 예술이다. 훌륭한 설계는 적절한 트레이드오프의 결과물이라는 사실을 명심하라. 이러한 트레이드오프 과정이 설계를 어려우면서도 흥미진진한 작업으로 만드는 것이다.-오브젝트 33p-

필자의 의견은

필자도 마찬가지로 의존성을 없애기 이해 결합도를 낮추는 방향이 더 중요하다고 생각한다. 그러나, Audience은 왜 Bag을 들고 다녀야하고, TicketSeller는 TicketOffice를 설정할 수 있도록 설계 되었는지는 아직 작가만 안다. 최소한의 비즈니스 문제를 해결하기 위해선 Audience와 Bag 객체가 하나로 합쳐저야 하고, TicketSeller 객체가 하나로 합쳐진다면 아래의 그림과 같이 확실하게 의존성을 제거할 수 있게 될 것이다. 그러나, 객체의 크기가 커지는 단점이 생기게 된다. 이는 장기적으로 봤을때 옳은지 아닌지는 책을 좀 더 보거나 경험을 더 쌓아야 알 수 있을 것 같다. 지금의 필자의 실력으로는 알지 못하는 영역이다.

Image title

그림 2 - 클래스 다이어그램 (필자 의견)


Comments