개발을 하다보면 기능 동작을 구현하고 나만 알기 쉬운 코드(암호문?) 가 된다. 각 객체에 의존성이 높아지며 스파게티 코드가 된다.
이번 우테코 5기 프리코스를 지원하여 미션을 진행하다 많이 부족한 부분들을 정리하고 오브젝트 책을 참고하며 글을 써봅니다.
먼저 소프트웨어의 모듈이 가져야하는 기능은 아래 3가지와 같다.
모듈이 가져야 하는 3가지 기능
- 제대로 동작해야 한다.
- 변경이 자유로워야 한다.
- 가독성이 좋아야 한다.
예제 코드를 보면서 이해해보자.
관람객이 가지고 있는 가방에 초대장이 있으면 티켓을 주고 없으면 티켓 금액을 지불하여 티켓을 구매하는 코드다.
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);
}
}
먼저 아래와 같이 문제점을 정리해보았다.
수동적인 코드
- 소극장이라는 객체가 티켓, 가방, 관람객, 티켓 판매장, 티켓 판매원 객체를 통제하여 수동적인 코드로 동작
변경이 어렵다.
- 현금이 아닌 카드로 결제를 진행한다면?
- 관람객이 가방을 항상 가지고 다니지 않는다면?
- 티켓 판매원이 티켓 판매장에서 판매를 하지 않고 밖에서 한다면?
예제 설계 개선 하기 따라하기
기존 코드에는 극장이 관람객과 판매원에 직접 접근하였다.
이는 관람객과 티켓판매원의 객체가 극장과 결합된다는 의미다. 따라서 관람객과 판매원의 코드를 변경한다면 극장의 코드도 변경이 이루어져야 하는 문제점이 있다.
문제를 해결하려면 객체를 자율적으로!
관람객이 가방을 가지고 있다는 사실과 팬매원이 매표소에서 티켓을 판매한다는 사실을 알 필요가 없다.
따라서 판매원이 스스로 티켓과 요금을 관리한다면 해결된다.
리팩터링 한 코드
public class Theater {
private TicketSeller ticketSeller;
public void enter(Audience audience) {
ticketSeller.sellTo(audience);
}
}
public class TicketSeller {
private TicketOffice ticketOffice;
public void sellTo(Audience audience) {
ticketOffice.sellTicketTo(audience);
}
}
public class TicketOffice {
private Long amount;
private List<Ticket> tickets = new ArrayList<>();
public TicketOffice(Long amount, List<Ticket> tickets) {
this.amount = amount;
this.tickets.addAll(tickets);
}
public void sellTicketTo(Audience audience) {
plusAmount(audience.buy(getTicket()));
}
// private method 생략
}
public class Audience {
private Bag bag;
public Long buy(Ticket ticket) {
return bag.hold(ticket);
}
}
public class Bag {
private Long amount;
private Ticket ticket;
private Invitation invitation;
public Long hold(Ticket ticket) {
if (hasInvitation()) {
receiveTicket(ticket);
return 0L;
} else {
minusAmount(ticket.getFee());
receiveTicket(ticket);
return ticket.getFee();
}
}
}
기존 코드의 문제점 변경 용이성과 가독성 각각의 객체가 책임을 가지며 능동적으로 동작되게 개선하였다.
아래 그림을 보면서 객체 간에 메시지 전송을 어떻게 하는지 각각의 책임의 이동을 살펴보자.