티켓 판매 어플리케이션 구현하기
public class Theater { private TicketSeller ticketSeller; public Theater(TicketSeller ticketSeller) { this.ticketSeller = ticketSeller; } public void enter(Audience audience) { if (audience.getBag().hasInvitation()) { Ticket ticket = ticketSeller.getTicketOffice().getTicket(); audience.getBag().setTicket(ticket); } else { Ticket ticket = ticketSeller.getTicketOffice().getTicket(); audience.getBag().minusAmount(ticket.getFee()); ticketSeller.getTicketOffice().plusAmount(ticket.getFee()); audience.getBag().setTicket(ticket); } }

로버트 마틴 < 클린 소프트웨어: 애자일 원칙과 패턴, 그리고 실천 방법 > 모든 소프트웨어 모듈에는 세 가지 목적이 있다. 1. 실행 중에 제대로 동작하는 것 2. 변경을 위해 존재하는 것 3. 코드를 읽는 사람과 의사소통하는 것
- 위의 코드는 변경 용이성과 읽는 사람과의 의사소통이라는 목적은 만족시키지 못함
변경 용이성 떨어짐
- 절차 지향적으로 작성되어, Theater 클래스에서 모든 내용을 다 알고 있음. TicketSeller와 Audience 내부의 모든 데이터를 이용하여 Theater가 로직을 작성하기 때문에 내부 로직을 변경할 때 Theater도 다 바뀌어야 함 ⇒ 객체의 자율성이 존중되지 않는 것임
- 위 코드는 하나의 클래스나 메서드에서 너무 많은 세부사항을 다루기 때문에 코드를 작성하는 사람뿐만 아니라 코드를 읽고 이해해야 하는 사람 모두에게 큰 부담을 줌
Audience와 TicketSeller 를 변경할 경우 Theater도 함께 변경해야 함!!!
⇒의존성
과 관련된 문제.의존성
은 변경에 대한 영향을 암시
- 객체 사이의 의존성이 과한 경우를 가리켜
결합도
가 높다고 말함
설계 개선하기
- 변경과 의사소통이라는 문제가 서로 엮여 있음. 코드를 이해하기 어려운 이유는 Theater가 관람객의 가방과 판매원의 매표소에 직접 접근하기 때문임. 이것은 관람객과 판매원이 자신의 일을 스스로 처리해야 한다는 우리의 직관을 벗어남 ⇒
객체를 자율적인 존재로 만들라
public class Theater { private TicketSeller ticketSeller; public Theater(TicketSeller ticketSeller) { this.ticketSeller = ticketSeller; } public void enter(Audience audience) { ticketSeller.sellTo(audience); } }

Theater
는TicketSeller
의인터페이스
에만 의존
TicketSeller
가 내부에TicketOffice
인스턴스를 포함하고 있다는 사실은 구현의 영역
- 객체를 인터페이스와 구현으로 나누고 인터페이스만을 공개하는 것은 객체 사이의 결합도를 낮추고 변경하기 쉬운 코드를 작성하기 위해 따라야 하는 가장 기본적인 설계원칙임
public class TicketSeller { private TicketOffice ticketOffice; public TicketSeller(TicketOffice ticketOffice) { this.ticketOffice = ticketOffice; } public void sellTo(Audience audience) { ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket())); } }
무엇이 개선됐는가
- 수정된
Audience
와TicketSeller
는 자신이 가지고 있는 소지품을 스스로 관리함
- 따라서 코드를 읽는 사람과의 의사소통이라는 관점에서 이 코드는 확실히 개선된 것임
Audience
나TicketSeller
의 내부 구현을 변경하더라도Theater
를 함께 변경할 필요가 없어진 것임
캡슐화와 응집도
- 핵심은 객체 내부의 상태를 캡슐화하고 객체 간에 오직 메시지를 통해서만 상호작용하도록 만드는 것임
- Theater는 TicketSeller의 내부에 대해서는 전혀 알지 못하고 TicketSeller의 sellTo 메시지를 이해하고 응답할 수 있다는 사실만 알고 있는 것
- 객체의 응집도를 높이기 위해서는 객체 스스로 자신의 데이터를 책임져야 함
책임의 이동
- 책임 은 기능을 가리키는 객체지향 세계의 용어


- 객체 지향세계에서는 독재자가 존재하지 않고 각 객체에 책임이 적절하게 분배됨 ⇒ 이런 관점에서 객체지향 프로그래밍을 흔히 데이터와 프로세스를 하나의 단위로 통합해 놓는 방식으로 표현하기도 함
- 데이터와 해당 데이터를 사용하는 프로세스가 별도의 객체에 위치하고 있다면 절차적 프로그래밍 방식을 따르고 있을 확률이 높음
설계를 어렵게 만드는 것은 의존성이라는 것을 기억하라. 해결 방법은 불필요한 의존성을 제거함으로써 객체 사이의 결합도를 낮추는 것이다. 예제에서 결합도를 낮추기 위해 선택한 방법은 Theater가 몰라도 되는 세부사항을 Audience와 TicketSeller 내부로 감춰 캡슐화하는 것임. 결과적으로 불필요한 세부사항을 객체 내부로 캡슐화하는 것은 객체의 자율성을 높이고 응집도 높은 객체들의 공동체를 창조할 수 있게 한다.
불필요한 세부사항을 캡슐화하는 자율적인 객체들이 낮은 결합도와 높은 응집도를 가지고 협력하도록 최소한의 의존성만을 남기는 것이 훌륭한 객체지향 설계다.
객체지향 설계
- 좋은 설계란 무엇인가? 우리가 짜는 프로그램은 두 가지 요구사항을 만족시켜야 한다
- 우리는 오늘 완성해야 하는 기능을 구현하는 코드를 짜야 하는 동시에 내일 쉽게 변경할 수 있는 코드를 짜야 한다. 좋은 설계란 오늘 요구하는 기능을 온전히 수행하면서 내일의 변경을 매끄럽게 수용할 수 있는 설계다