조영호님의 책 '오브젝트: 코드로 이해하는 객체지향 설계' 를 통해 객체지향 설계에 대한것을 익혀보고자 한다.
처음에 이론보다 실무의 중요성을 강조하며 시작했고 나또한 동의한다.
이번 챕터에서는 티켓 판매 애플리케이션을 구현하며 객체지향 설계에 대해 알아보자.
Theater 클래스 이외 다른 클래스들은 생략했고 필요하다면 아래 레포지토리를 통해 확인하면 좋겠다.
https://github.com/ruudska6/Object/tree/master/src/chapter1/ticketsellapp
Object/src/chapter1/ticketsellapp at master · ruudska6/Object
Contribute to ruudska6/Object development by creating an account on GitHub.
github.com
Theater 클래스다.
public class Theater {
private TicektSeller ticektSeller;
public Theater(TicektSeller ticektSeller) {
this.ticektSeller = ticektSeller;
}
public void enter(Audience audience) {
//초대장이 있다면
if (audience.getBag().hasInvitation()) {
Ticket ticket = ticektSeller.getTicketOffice().getTicket();
audience.getBag().setTicket(ticket);
//초대장이 없다면
} else {
Ticket ticket = ticektSeller.getTicketOffice().getTicket();
audience.getBag().minusAmount(ticket.getFee());
ticektSeller.getTicketOffice().plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
}
그러나 이렇게 설계한 코드에는 문제가 있다.
Theater 클래스가 여러 클래스들을 의존하고 있는데
이러한 것을 결합도가 높다한다. 결합도가 높을 수록 함께 변경 될 확률도 높아지기 때문에 최대한 낮춰야한다.
모듈이란? 클래스나 패키지,라이브러리와 같이 프로그램을 구성하는 임의의 요소
소프트웨어 모듈의 세가지 목적
1. 실행중에 제대로 동작해야한다.
2. 변경을 위해 존재하는 것다.
3. 코드를 읽는 사람과 의사소통해야한다.
앞서 작성한 코드는 1번만 충족하고 나머지는 충족시키지 못한다.
왜 문제가 있을까?
첫째, 우리가 작성한 코드는 관람객과 판매원이 소극장의 통제를 받는 수동적인 존재라고 가정했다. 이것은 현실 세계의 우리 상식과는 많이 다르다. 현실에서는 관람객이 직접 가방을 열고 돈을 주고 판매원이 직접 티켓을 관리하는 데 이 코드에서는 모든 것을 소극장이 제어하고 있어 이해하기 어려워 3번에 위배된다.
둘째, 변경에 취약하다. 예를들어 관람객이 가방을 들고 있지 않는다면 audience 클래스에서 Bag을 제거해줄 뿐만 아니라 Theater에서 enter 메서드 역시 변경해주어야한다. 이런식으로 코드가 서로 의존성을 갖고 있다면 코드를 변경하기 어렵게 만든다. 하나만 변경하면 다른데서 오류가 발생한다. 그래서 2번에 위배된다.
이해 하기 쉽고 변경에 용이하게 하려면 어떡해야할까?
Theater의 통제를 받지 않도록 관람객과 판매원을 자율적인 존재로 만들어주면 된다. 어떻게?
관람객이 직접 Bag을 관리할 수 있도록 해주고, 판매원이 직접 Ticket을 관리하면 된다. 이렇게 객체에게 자율성을 부여하면 현실의 상식과도 일치해 이해하기 쉬워지고 변경하기 쉬워져서 해결 된다.
자율적인 존재로 어떻게 만들까?
Theater의 enter 메서드에 있던 로직을 TicketSeller에 sellTo로 옮긴다.
캡슐화란 개념적이나 물리적으로 객체 내부의 세부적인 사항을 감추는 것을 말함.
캡슐화의 목적은 변경하기 쉬운 객체를 만드는것이다. 캡슐화를 통해 객체 내부로의 접근을 제한하면 객체와 객체 사이의 결합도를 낮출 수 있기때문에 설계를 좀더 쉽게 변경할 수 있게 됨.
public class TicketSeller {
private TicketOffice ticketOffice;
public TicketSeller(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
/* 캡슐화를 위해 제거함.
public TicketOffice getTicketOffice() {
return ticketOffice;
}
*/
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);
}
}
}
객체 내부의 상태를 캡슐화하고 오직 메세지를 통해서만 상호작용을 하도록 만든다. 캡슐화를하면 결합도를 낮출 수 있다.
이제 Theater의 enter 메서드는 단순히 ticketSeller의 sellTo 메서드를 호출하는 역할만 한다.
public void enter(Audience audience) {
ticketSeller.sellTo(audience);
}
그러나 이것은 Bag 인스턴스에 접근하는 객체가 Theater에서 TicketSeller로 바뀌었을 뿐이다.
Audience는 여전히 자율적인 존재가 아니다. 동일한 방법으로 캡슐화를 할 수 있다.
Audience에 buy 메서드를 추가하고 TicketSeller의 sellTO 메서드에서 getBag 메서드에 접근하는 부분을 buy에 옮기면 된다.
public Long buy(Ticket ticket) {
if (bag.hasInvitation()) {
bag.setTicket(ticket);
return 0L;
} else {
bag.setTicket(ticket);
bag.minusAmount(ticket.getFee());
return ticket.getFee();
}
}
sellTo 변경
public void sellTo(Audience audience) {
ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket()));
}
이렇게함으로써 TicketSeller와 Audience 사이의 결합도가 낮아졌다. 이 말은 Audience의 구현을 수정하더라도 TicketSeller에는 영향을 미치지않는 자율적인 존재가 되었다는 것이다.
이러한 것은 절차지향으로 짰던 코드를 객체지향으로 바꾸는 과정이다. 절차지향은 책임이 한 곳에 몰려 있어 변경하기 어렵고 이해하기도 어렵다. 그렇기 때문에 객체지향 방식이 더 낫다고 할 수 있다.
내가 작성한 코드가 객체지향 프로그래밍 방식을 따르고 있는지 어떻게 알 수 있을까? 일단은 쉽게 이해하기 위해
데이터와 데이터를 사용하는 프로세스가 동일한 객체 안에 위치한다면 객체 지향 프로그래밍 방식을 따르고 있다고 생각하면 되겠다.
모든 클래스에 자율성을 부여한다면 최고의 결과가 나올 수 있을까?
그렇지 않다. 의존성이 추가되어 오히려 결합도가 높아질 수 있다.
자율성과 결합도는 상충 관계에 있다고 할 수 있다. 적절한 타협점을 찾아서 최소한의 의존성을 유지할 수 있도록 하자.
이런 적절한 타협점을 찾는 부분이 설계의 굉장히 어려운 부분이라고 할 수 있다. 프로젝트를 할때 이런식으로 설계를 해보는 경험이 없으면 배울 수 없는 부분이다.
현실 세계의 상식
아까부터 계속 현실세계의 상식을 얘기했는데 Bag에 자율성을 주고 TicketOffice에 자율성을 주는 것은
가방이 어떻게 자기 안에 있는 돈을 꺼내서 줄 수 있는지 의문이 생긴다. 하지만 객체 지향의 세계에서는 현실세계에서 수동적인 존재라고 하더라도 능동적이고 자율적인 존재라고 가정한다. 이것을 의인화 라고한다.
'Java' 카테고리의 다른 글
[오브젝트 스터디] Chapter 05 책임 할당하기 (3) | 2025.01.07 |
---|---|
[오브젝트 스터디] Chapter 03 역할, 책임, 협력 (1) | 2024.12.27 |
[오브젝트 스터디] Chapter 02 객체지향 프로그래밍 (3) | 2024.12.15 |
인프런 김영한 실전 자바 기본 편 후기 (4) | 2024.10.11 |