오브젝트 - 01. 객체, 설계

강준혁·2022년 8월 19일
0

오브젝트

목록 보기
2/14
post-thumbnail

이 장에서는 티켓 판매 어플리케이션 예제를 통해 객체지향의 기본적인 개념에 대해 설명한다.

어플리케이션의 간단한 명세는 다음과 같다.

  • 이벤트에 당첨된 관람객은 가지고 있는 초대장을 티켓으로 교환한 뒤 입장할 수 있다.
  • 그렇지 않은 관람객은 티켓을 구매한 뒤에 입장할 수 있다.

절차적 프로그래밍으로 구현

이를 절차적으로 구현한 코드는 다음과 같다.

    public class Theater {
        private TicketSeller ticketSeller;

        public Theater(TicketSeller ticketSeller) {
            this.ticketSeller = ticketSeller;
        }

        public void enter(Audience audience) {
            if (audience.getBag().hasInvitation()) {
                Ticket ticket = ticketOffice.getTicket();
                audience.getBag().setTicket(ticket);
            } else {
                Ticket ticket = ticketOffice.getTicket();
                audience.getBag().minusAmount(ticket.getFee());
                ticketOffice.plusAmount(ticket.getFee());
                audience.getBag().setTicket(ticket);
            }
        }
    }

위 코드에서 Theater 의 enter 메서드는 프로세스이며 Audience, TicketSeller, Bag, TicketOffice 는 데이터 이다. 이처럼 프로세스와 데이터를 별도의 모듈에 위치시키는 방식을 절차적 프로그래밍이라 한다.

무엇이 문제인가

로버트 마틴의 클린 소프트웨어에서 언급된 소프트웨어 모듈이 가져야 하는 세가지 기능을 다음과 같이 설명한다.
1. 실행 중에 제대로 동작한다.
2. 변경을 위해 존재한다, 간단한 작업만으로 변경이 가능해야 한다.
3. 작성된 코드는 쉽게 읽고 이해할 수 있어야 한다.

위 코드는 1. 은 충족하지만 2. 와 3. 을 충족시키지 못한다.

예상을 빗나가는 코드

위 코드가 하는 기능을 풀어서 쓰면 다음과 같다.

극장(Theater) 은 관람객이 가진 가방에서 초대장이 있는지 없는지 확인한다.
초대장이 있으면 관람객의 가방 안에 티켓을 집어넣는다.
초대장이 없는 경우 티켓판매소에서 티켓을 꺼낸 뒤, 관람객의 가방안의 현금을 감소시키고 
티켓 판매소의 현금을 증가시킨 뒤 관람객의 가방 안에 티켓을 집어넣는다.

이해하기 쉬운, 예상 가능한 코드는 우리가 일반적으로 생각하는 현실에서 크게 벗어나지 않아야 한다.

극장이 마음대로 관람객의 가방을 뒤지고, 현금을 꺼내거나 티켓을 집어넣는 것은 일반적이지 않다.

변경에 취약한 코드

위 예시에서 극장은 관람객이 가방을 가지고 있으며 그 안에 현금과 초대장이 존재한다는 사실을 가정한다.

만약 가방이 아닌 주머니에 들어있을 수도 있다면?

극장은 관람객의 세부사항까지 과도하게 의존을 하고있기에 변경에 자유롭지 못하다.

이는 객체 사이의 의존성과 관련된 문제이다.
하지만 의존성을 완전히 없애는 것이 정답은 아니다.

객체지향 설계는 서로 의존하면서 협력하는 객체들의 공동체를 구축하는 것이다.
따라서 우리는 어플리케이션의 기능을 구현하는 데 필요한 최소한의 의존성만 유지하고 불필요한 의존성을 제거해야 할 필요가 있다.

객체간의 결합도를 최대한 낮추어 객체의 변경이 전파되는 영역을 최소화 함으로서 변경이 용이한 설계를 해야 한다.

설계 개선하기

관람객과 판매원을 자율적인 존재로 만든다.

    public class Theater {
        private TicketSeller ticketSeller;

        public Theater(TicketSeller ticketSeller) {
            this.ticketSeller = ticketSeller;
        }

        public void enter(Audience audience) {
            ticketOffice.sellTo(audience);
        }
    }

기존의 TicketOffice 는 그저 Theater 가 시키는 대로 하는 수동적인 존재였다면, 위 코드에서는 티켓 판매에 대한 책임을 가지고 자율적으로 수행한다.

Theater 는 티켓 판매의 세부적인 구현에 대해서는 알지 못한다.
그저 TicketOffice 의 티켓 판매 인터페이스에 대해서만 알 뿐이다.

객체를 인터페이스와 구현으로 나누고 인터페이스만을 공개하는 것은 객체 사이의 결합도를 낮추고 변경하기 쉬운 코드를 작성하기 위해 따라야 하는 가장 기본적인 설계 원칙이다.

위 수정된 코드에서는 데이터와 프로세스를 TicketOffice 로 이동시켰다.

이처럼 절차지향과는 다르게, 데이터와 프로세스가 동일한 모듈 내부에 위치하도록 프로그래밍 하는 방식을 객체지향 프로그래밍이라 한다.

profile
백엔드 개발자

0개의 댓글