수정하기 전, Theater의 enter 메서드 내에 Audience, TicketSeller로 부터 Bag, TicketOffice를 가져와 관람객을 입장시키는 절차를 구현함
이처럼 프로세스와 데이터를 별도의 모듈에 위치시키는 방식을 절차적 프로그래밍이라고 부름
일반적으로 절차적 프로그래밍은 우리의 직관에 위배됨
절차적 프로그래밍은 예상을 쉽게 벗어나기에 코드를 읽는 사람과 원활한 의사소통이 불가능
더 큰 문제는 절차적 프로그래밍에서는 데이터의 변경으로 인한 영향을 지역적으로 고립시키기 어려움
따라서 절차적 프로그래밍은 변경하기 어려운 코드를 양산하는 경향이 있음
변경하기 쉬운 설계는 한번에 하나의 클래스만 변경할 수 있는 설계지만 절차적 프로그래밍은 프로세스가 필요한 모든 데이터에 의존해야 한다는 근본적인 문제점 때문에 변경에 취약할 수 밖에 없음
해결 방법: 자신의 데이터를 스스로 처리하도록 프로세스의 적절한 단계를 Audience, TicketSeller로 이동
이처럼 데이터와 프로세스가 동일한 모듈 내부에 위치하도록 프로그래밍하는 방식을 객체지향 프로그래밍이라고 함
객체지향 설계의 핵심은 캡슐화를 이용해 의존성을 적절히 관리함으로써 객체 사이의 결합도를 낮추는 것
객체지향과 절차지향 방식의 근본적인 차이를 만드는 것은 책임의 이동(shift of resposiblity)임
두 방식의 차이를 쉽게 이해할 수 있는 방법: 기능을 처리하는 방법을 살펴보기
Theater에 몰려있던 책임이 각 객체로 이동한 것을 책임의 이동이라고 함
객체지향 설계에서는 독재자가 없고 각 객체가 스스로 책임지는 구조임
객체와 객체는 협력하면서 특정한 역할을 수행하는데 필요하고 적절한 책임을 수행해야 함
설계를 어렵게 만드는 것 - 의존성
해결 방법: 의존성을 제거함으로써 객체 사이의 결합도를 낮추는 것
불필요한 세부사항을 객체 내부로 캡슐화하여 객체의 자율성을 높이고 응집도 높은 객체들의 공동체를 만들 수 있도록 함
변경 후의 코드에서 TicketOffice와 Audience 사이에 의존성이 새롭게 추가됨
(TicketOffice가 Audience에게 직접 티켓을 판매하므로 관람객을 알고 있어야 함)
의존성의 추가는 높은 결합도를 의미하고 높은 결합도는 변경하기 어려운 설계를 의미함
TicketOffice의 자율성은 높였지만 전체 설계의 관점에서는 결합도가 상승해버림
둘다 만족시키는 방법이 잘 떠오르지 않자 개발팀은 TicketOffice의 자율성보다 Audience에 대한 결합도를 낮추는 것이 더 중요하다는 결론을 지음
첫째, 어떤 기능을 설계하는 방법은 한가지 이상일 수 있음
둘째, 동일한 기능을 여러가지로 설계 가능하므로 결국 설계는 트레이드오프의 산물임(모든 경우를 만족시킬 설계는 만들기 어려움)
설계는 균형의 예술이고 훌륭한 설계는 적절한 트레이드오프의 결과물
현실에선 수동적인 존재였지만 객체지향 세계에서는 모든 것이 능동적이고 자율적인 존재로 바뀜
레베카 워프스브록은 이처럼 능동적이고 자율적인 존재로 소프트웨어 객체를 설계하는 원칙을 의인화라고 부름
훌륭한 객체지향 설계는 소프트웨어를 구성하는 모든 객체들이 자율적으로 행동하는 설계를 말함
그 대상이 실세계에선 수동적인 존재여도 일단 객체지향 세계로 넘어오면 모두다 생명을 가진 존재로 탈바꿈 함
설계의 정의
설계란 코드를 배치하는 것
좋은 설계: 오늘의 요구하는 기능을 온전히 수행하면서 내일의 변경을 매끄럽게 수용할 수 있는 설계
즉, 요구사항의 변경은 코드 수정을 초래하며 코드 수정은 버그 발생 가능성을 높이며 버그 발생 가능성은 코드 수정 의지를 깎아 먹음
따라서 진짜 원하는 것은 변경에 유연하게 대응할 수 있는 코드임
객체지향 프로그래밍은 의존성을 효율적으로 통제할 수 있는 다양한 방법을 제공함으로써 요구사항 변경에 좀더 수월하게 대응할 수 있는 가능성을 보여줌
변경 가능한 코드 = 이해하기 쉬운 코드
객체지향 패러다임은 내가 세상을 바라보는 방식대로 코드를 작성할 수 있도록 도움
애플리케이션의 기능 구현을 위해 객체들이 협력하는 과정 속에서 객체들은 다른 객체에 의존하게 됨
훌륭한 객체지향 설계는 협력하는 객체 사이의 의존성을 적절히 관리하는 설계
객체 간의 의존성은 애플리케이션을 수정하기 어렵게 만드는 주범