객체, 설계
in Dev-Study on Architecture
공부하는 내용을 정리하는 목적으로 작성하고 있습니다. 잘못 작성된 내용을 지적해주시면 좀더깊이 공부해서 내용을 수정하겠습니다.
프로그래밍 패러다임
과거: 표준적인 모델을 따르거나 모방하는 상황을 가리킬때 사용
현대: 한 시대의 사회 전체가 공유하는 이론이나 방법, 문제의식 등의 체계를 의미
프로그래밍 패러다임의 중요한 의미
- 개발자 공동체가 동일한 프로그래밍 스타일과 모델을 공유함으로써 불필요한 의견 충돌 방지
- 프로그래밍 패러다임을 교육함으로써 동일한 규칙과 방법을 공유하는 개발자로 성장 가능
패러다임 전환 (Paradigm Shift)
과거의 패러다임이 새로운 패러다임에 의해 대체됨으로써 이론/관점이 완전히 달라지는 현상
프로그래밍 패러다임이 변경되었다고해서 과거의 패러다임이 폐기되지는 않음
과거에 있던 패러다임의 단점을 잘 보완하여 발전하는 형태로 진행됨
- 절차지향 프로그래밍의 경우 비지니스 로직이 하나로 뭉쳐져 다양한 변화에 대응하기 어려웠다면
객체지향 프로그래밍은 객체들의 책임에따라 로직을 분산시킴으로써 변화에 유연해졌다.
객체, 설계
티켓 판매 어플리케이션 구현
요구사항
- 관람객이 소극장에 입장하려면 반드시 티켓이 필요하다.
- 티켓은 현금으로 구매하거나 미리 발급된 초대장으로 교환할 수 있다.
- 관람객객은 매표소 내에 있는 판매원을 통해 티켓을 구매하거나 교환할 수 있다.
- 관람객은 현금/초대장/티켓을 가방에 소지할 수 있다.
- 판매원은 티켓을 판매하면 금액을 적립한다.
요구사항에 따라 어플리케이션을 구매했고 구현한 클래스 다이어그램은 아래와 같다.
첫번째 구현의 문제점
소극장 객체가 너무 많은 책임을 지고 있다.
- 입장한 관람객의 가방을 뒤져 초대장 소지 여부 확인
- 초대장이 있다면 판매원의 티켓보관함을 뒤져 티켓을 꺼내고 관람객의 가방에 집어넣는다.
- 초대장이 없다면 판매원의 티켓보관함을 뒤져 티켓을 꺼내고 관람객의 가방을 뒤져 현금을 빼온다.
티켓 금액을 적립한 뒤 관람객의 가방에 티켓을 집어넣는다.
// 소극장의 입장 메서드 구현부
void enter(Audience audience) {
// 초대장이 있는 경우
if (audience.getBag().hasInvitation()) {
// 초대장과 티켓이 교환되므로 티켓은 1장 줄어들지만 매출은 발생하지 않는다.
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().setTicket(ticket);
}
// 초대장이 없는 경우
else {
// 관람객이 티켓을 구매하므로 티켓은 1장 줄어들고, 매출이 발생한다. 관람객이 소지한 현금도 티켓가격만큼 줄어든다.
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().minusAmount(ticket.getFee());
ticketSeller.getTicketOffice().plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
사실 소극장 객체는 관람객을 입장시키기만 하면 될 뿐, 티켓이 얼마인지 관람객의 가방에 무엇이 들어있는지 등등 관심을 가질 필요가 없다.
티켓 판매/교환은 관람객과 판매원의 책임이지 소극장이 관여할 바가 아니다.
관람객 외에 어느 누구도 관람객의 가방에 접근하는 것 자체가 문제다. (누가 내 가방에 손을 댄다고?)
만약 관람객이 가방을 가지고 있지 않다면? 소극장, 판매원, 관람객 객체 모두 변경이 필요해진다.
설계 변경
- 소극장에 관람객이 입장하면 판매원에게 판매에 대한 책임을 위임한다.
- 판매원은 매표소에 티켓판매에 대한 책임을 위임한다. (티켓에 대한 접근은 판매자가 아닌 매표소가 담당한다.)
- 매표소는 티켓을 한장꺼내고 관람객에게 판매한다.
- 관람객은 티켓을 구매하기 위한 준비가 되었는지(현금이 있거나 초대장이 있거나) 가방에게 넘기고 티켓을 전달한다.
- 가방은 초대장이 있으면 티켓을 소지하고 끝낸다.
- 가방은 초대장이 없으면 티켓을 소지하고 티켓 금액만큼 현금을 내보낸다.
설게 변경 후 결과
티켓 판매 프로세스를 각 객체들이 책임을 나누었고 접근이 불필요한 의존성을 줄여 결합도를 낮추었다.
- 소극장은 관람객, 판매원, 매표소, 가방 등 모든 객체에 접근하였으나 변경결과 관람객과 판매원에게만 의존하게 되었다.
- 판매원은 판매를 위해 관람객 뿐 아니라 관람객의 가방에도 접근하였으나 변경결과 매표소에 판매 기능을 위임하여 가방에 대한 의존성을 줄였다.
- 가방은 관람객이 필요로 할때 데이터만 전달해주는 역할이었으나 가방이 초대장 소지여부를 확인하고 현금을 지불하는 역할을 능동적으로 수행하게 되었다.
캡슐화
외부 객체의 불필요한 접근을 막기위해 세부적인 기능/구현부를 감추는 작업
외부 객체는 필요한 정보를 위해 메시지만 전달하고(함수 호출) 요청한 메시지를 처리하는 책임은 내부 구현에 맡긴다.
외부 객체는 요청한 메시지의 결과만 받으면 되며 내부에 어떤식으로 구현되었는지는 알 필요가 없어짐으로써 변경에 용이해진다.
응집도
밀접하게 연관된 작업만 수행하고 연관성 없는 작업은 다른 객체에게 위임할경우
절차지향 프로그래밍
프로세스와 데이터를 별도의 모듈에 위치하는 방식
티켓판매 어플리케이션 1번째의 경우 로직 처리는 소극장의 enter메서드에 집중되어 있으며 각 객체들은 데이터를 제공해주는 역할만 수행한다.
객체지향 프로그래밍
프로세스와 데이터가 동일한 모듈 내부에 위치하는 방식
티켓판매 어플리케이션 2번째의 경우 각 객체들이 처리할 로직과 필요한 데이터를 갖고 있다.
(적절한 객체에 적절한 책임을 할당한다.)
객체간의 결합도를 낮추는 방법으로는 세부 구현을 캡슐화하는 방법이 있다.
구현을 캡슐화하면 외부로부터의 접근이 줄어들게되므로 객체의 응집도와 자율성이 올라갈 수 있다.
좋은 설계를 위한 방법은 단 한가지만 존재하지는 않는다.
캡슐화를 통해 구현을 숨기다보면 기존에 없던 다른 객체와의 의존성이 추가될 수도 있다.
- 예제에서 관람객에게 티켓을 판매하고 매출을 관리하는 책임은 본래 판매원에게 있었으나 이 책임을 매표소로 위임함으로써 매표소는 기존에 없던 관람객과의 의존성이 추가되었다.
설계의 방법이 다수일경우 팀원들과 협의하여 가장 좋은 방식을 선택하면 된다. (정답은 없다.)
Object (조영호 저) 도서를 통해 학습한 내용을 정리하였습니다.