🔖 오늘 읽은 범위 : 1장 객체, 설계 (9~35p)
public class Theater {
// ..
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);
}
}
}
=> 를 TicketSeller 내부로 숨긴다
public class TicketSeller {
// ..
public void sellTo(Audience audience) {
if (audience.getBag().hasInvitation()) {
Ticket ticket = ticketOffice.getTicket();
audience.getBag().setTicket(ticket);
} else {
Ticket ticket = ticketOffice.getTicket();
audience.getBag().minusAmount(ticket.getFee());
ticketOffice.plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
}
public class Theater {
private TicketSeller ticketSeller;
public Theater(TicketSeller ticketSeller) {
this.ticketSeller = ticketSeller;
}
public void enter(Audience audience) {
ticketSeller.sellTo(audience);
}
}


public class TicketSeller {
// ..
public void sellTo(Audience audience) {
if (audience.getBag().hasInvitation()) {
Ticket ticket = ticketOffice.getTicket();
audience.getBag().setTicket(ticket);
} else {
Ticket ticket = ticketOffice.getTicket();
audience.getBag().minusAmount(ticket.getFee());
ticketOffice.plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
}
=>
public class Audience {
private Bag bag;
public Audience(Bag bag) {
this.bag = bag;
}
public Long buy(Ticket ticket) {
if (bag.hasInvitation()) {
bag.setTicket(ticket);
return 0L;
} else {
bag.setTicket(ticket);
bag.minusAmount(ticket.getFee());
return ticket.getFee();
}
}
}
Audience 는 자신의 가방안에 초대장이 있는지 스스로 확인
직접 처리하므로 더 이상 Audience 가 Bag 을 소유하고 있다는 사실을 외부에 알림 필요 없음
TicketSeller 가 Audience 의 인터페이스에만 의존하도록 수정
public class TicketSeller {
private TicketOffice ticketOffice;
public TicketSeller(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
public void sellTo(Audience audience) {
ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket()));
}
}



public class Bag {
private Long amount;
private Ticket ticket;
private Invitation invitation;
public Long hold(Ticket ticket) {
if (hasInvitation()) {
setTicket(ticket);
return 0L;
} else {
setTicket(ticket);
minusAmount(ticket.getFee());
return ticket.getFee();
}
}
private void setTicket(Ticket ticket) {
this.ticket = ticket;
}
private boolean hasInvitation() {
return invitation != null;
}
private void minusAmount(Long amount) {
this.amount -= amount;
}
}
public class Audience {
public Long buy(Ticket ticket) {
return bag.hold(ticket);
}
}
public class TicketSeller {
public void sellTo(Audience audience) {
ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket()));
}
}
public class TicketOffice {
public void sellTicketTo(Audience audience) {
plusAmount(audience.buy(getTicket()));
}
private Ticket getTicket() {
return tickets.remove(0);
}
private void plusAmount(Long amount) {
this.amount += amount;
}
}
public class TicketSeller {
public void sellTo(Audience audience) {
ticketOffice.sellTicketTo(audience);
}
}

| 구분 | Association (실선) | Dependency (점선) |
|---|---|---|
| 지속성 | 지속적인 관계 (필드로 참조) | 일시적인 관계 (메서드 호출 시 사용) |
| 구조적 여부 | 구조적 (클래스 설계에 반영됨) | 비구조적 (특정 상황에서만 사용) |
| 코드에서 표현 | 클래스 필드(멤버 변수)로 객체를 가짐 | 메서드 매개변수, 지역 변수로 객체를 사용 |
| UML 표현 | 실선 | 점선 |
| 예시 비유 | 사람과 자동차 (소유 관계) | 사람과 공구 (사용 관계) |
결합도 높은 설계:
public class TicketOffice {
public void sellTicket(Audience audience) {
if (audience.getBag().hasInvitation()) {
audience.getBag().setTicket(new Ticket());
} else {
audience.getBag().minusAmount(10000);
audience.getBag().setTicket(new Ticket());
}
}
}
여기서 TicketOffice는 Audience의 Bag 객체에 직접 접근하며, hasInvitation, minusAmount 같은 내부 로직에 의존합니다. CBO가 높고, Audience의 구현이 바뀌면 TicketOffice도 수정해야 합니다.
결합도 낮춘 설계:
public class TicketOffice {
public void sellTicket(Audience audience) {
audience.buy(new Ticket());
}
}
public class Audience {
private Bag bag;
public void buy(Ticket ticket) {
if (bag.hasInvitation()) {
bag.setTicket(ticket);
} else {
bag.minusAmount(10000);
bag.setTicket(ticket);
}
}
}
이 경우 TicketOffice는 Audience의 buy라는 인터페이스만 호출하며, 내부 구현은 Audience가 처리합니다. CBO가 낮아지고, Audience의 변경이 TicketOffice에 영향을 덜 미칩니다.
결론적으로, 결합도와 자율성을 정량화하려면 CBO, 호출 횟수, 의존성 그래프 같은 메트릭을 활용할 수 있습니다. 하지만 책에서 강조하듯, "결합도를 낮추는 것"이 설계의 단순성, 유지보수성, 재사용성 측면에서 더 큰 가치를 제공한다면, 이를 우선시하는 것이 합리적일 수 있습니다. 정량화는 판단의 보조 도구일 뿐, 최종 결정은 설계의 목적과 요구사항에 따라 달라집니다.