책에서는 JAVA 로 예제를 진행해서 Typescript 로 변경해 보았다.
import { TicketSeller } from './TicketSeller';
import { Audience } from './Audience';
import { Ticket } from './Ticket';
export class Theater {
private ticketSeller: TicketSeller;
constructor(ticketSeller: TicketSeller) {
this.ticketSeller = ticketSeller;
}
public enter(audience: Audience): void {
if (audience.getBag().hasInvitation()) {
const ticket: Ticket = this.ticketSeller.getTicketOffice().getTicket();
audience.getBag().setTicket(ticket);
} else {
const ticket: Ticket = this.ticketSeller.getTicketOffice().getTicket();
audience.getBag().minusAmount(ticket.getFee());
this.ticketSeller.getTicketOffice().plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
}
수정하기 전 모습의 Theater 클래스. 애플리케이션이 정상 작동은 하지만 많은 문제가 있다고 함. 일단 이 코드를 한글로 풀어보면
소극장은 관람객의 가방을 열어 초대장이 있는지 확인한다. 초대장이 있으면 판매원은 매표소에 있는 티켓을 관람객의 가방 안으로 옮긴다. 초대장이 없다면 티켓 금액만큼의 현금을 관람객의 가방에서 꺼내 매표소에 적립한 후 티켓을 관람객의 가방 안으로 옮긴다.
한글로 풀어봐도 되게 어색한 상황이라서 그런지 코드도 많은 문제가 있는 것 같다. 여러가지 문제가 존재하지만 그중에서 가장 심각한 문제는 Audience 와 TicketSeller 를 변경할 경우 Theater 도 변경해야 한다는 사실이다. 의존성이 매우 높은 코드라고 할 수 있으며, 의존성이 높을수록 유지보수가 힘들어지고 코드를 이해하기 힘들어진다. (Theater 를 통해 모든 작업이 이루어지고 있기 때문!)
위 코드의 문제점을 해결 하는 방법은 높은 의존성을 낮춰주면 되는 것이다.
- Audience 가 직접 Bag 을 처리할 수 있도록 하기
- TicketSeller 가 직접 TickketOffice 를 처리할 수 있도록 하기
- Theater 는 관람객의 입장 여부만 관리할 수 있게 하기
import { TicketSeller } from './TicketSeller';
import { Audience } from './Audience';
export class Theater {
private ticketSeller: TicketSeller;
constructor(ticketSeller: TicketSeller) {
this.ticketSeller = ticketSeller;
}
public enter(audience: Audience): void {
this.ticketSeller.sellTo(audience);
}
}
Theater 는 관람객의 입장에만 관여하도록 코드를 위와 같이 수정한다.
import { TicketOffice } from './TicketOffice';
import { Audience } from './Audience';
export class TicketSeller {
private ticketOffice: TicketOffice;
constructor(ticketOffice: TicketOffice) {
this.ticketOffice = ticketOffice;
}
sellTo(audience: Audience) {
this.ticketOffice.sellTicketTo(audience);
}
}
TicketSeller 는 외부에서 매표소의 접근을 차단하고 직접 접근할 수 있게 변경.
import { Bag } from './Bag';
import { Ticket } from './Ticket';
export class Audience {
private bag: Bag;
constructor(bag: Bag) {
this.bag = bag;
}
buy(ticket: Ticket) {
return this.bag.hold(ticket);
}
}
Audience 는 자신의 가방 안에 초대장이 들어 있는지 스스로 확인하며 외부에서 가방을 열어보도록 허용하지 않는다. 외부에서는 Audience 가 가방을 가지고 있는지 없는지 관심을 가질 필요가 없다.
import { Invitation } from './Invitation';
import { Ticket } from './Ticket';
export class Bag {
private amount: number;
private invitation: Invitation;
private ticket: Ticket;
constructor() {}
hold(ticket: Ticket): number {
if (this.hasInvitation()) {
this.setTicket(ticket);
return 0;
}
this.setTicket(ticket);
this.minusAmount(ticket.getFee());
return ticket.getFee();
}
private hasInvitation(): boolean {
return this.invitation !== undefined;
}
private hasTicket(): boolean {
return this.ticket !== undefined;
}
private setTicket(ticket: Ticket) {
this.ticket = ticket;
}
private minusAmount(amount: number) {
this.amount -= amount;
}
private plusAmount(amount: number) {
this.amount += amount;
}
}
마지막으로 Bag 역시 행위(hold)를 가질 수 있게 수정해 응집도를 높여줄 수 있도록 하자. 응집도란 연관성 없는 작업은 다른 객체에게 위임하고 밀접하게 연관된 작업만을 수행하는 객체를 말한다. hold 를 통해서만 다른 객체와 메시지를 주고받으며 그 외에 자신이 가진 데이터는 자기가 책임지는 것이다.
결론: 설계를 어렵게 만드는 것은 의존성이다. 의존성을 제거해 결합도를 낮추자!
최근댓글