이 글은 오브젝트 책 1장을 읽고 정리한 내용입니다.
먼저 실습한 코드를 보면서 설명을 해보겠다.
실습 내용
추첨을 통해 선정된 관람객에게 공연을 무료로 관람할 수 있는 초대장을 발송하는 것이다.
다음 코드는 책에 1장 챕터 1에 나오는 예시이다.
https://github.com/RicardoKim/CodePracticeForObject/tree/0584127153da5c40b75d991af28b0aaa14fc2882
책에서 언급하는 소프트웨어 모듈이 가져야하는 3가지 목적은 다음과 같다.
- 돌아가야한다.
- 간단한 작업만으로 변경이 가능해야한다.
- 코드를 읽는 사람이 쉽게 읽을 수 있어야한다.
위의 코드는 돌아가기 때문에 첫번째 조건을 만족한다.
하지만 두번째랑 세번째는 이야기를 조금 해봐야한다.
먼저 Theater에 enter부분, 즉 우리의 비지니스 로직이 실행되는 부분의 코드를 보자.
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);
}
}
여기서 로직을 말로 풀어보자면
소극장은 관람객의 가방을 열어 그 안에 초대장이 들어있는지 살펴본다.
가방안에 초대장이 있으면 판매원은 매표소에 보관돼 있는 티켓을 관람객의 가방 안으로 옮긴다.
가방 안에 초대장이 들어있지 않다면 관란객의 가방에서 티켓의 금액만큼의 현금을 꺼내 매표소에 적립한 후에 매표소에 보관돼 있는 티켓을 관람객 가방안으로 옮긴다.
문제는 audience와 ticketseller가 수동적인 존재라는 것이다.
관람객은 티켓판매소에 도착하자마자 가방을 검사당하고 이후 관련된 모든 상황은 티켓판매소가 진행한다. 관람객은 어떤 행위자가 아닌 데이터를 담는 그릇밖에 되지 못한다.
티켓판매원도 마찬가지이다. 이들 또한 어떤 행위를 하는 주체자라기 보다는 ticketOffice를 호출하는 객체 그 이상 그 이하도 아니다.
즉 이 코드는 가독성이 떨어진다는 것이다. 더군다나 코드를 읽기위해서는 각 모듈이 어떤 필드를 가지고 있는지 또한 알아야한다.
그럼 3번째 조건에는 그다지 부합하지 않는것을 파악할 수 있다.
다른 코드를 또 살펴보자.
public class Audience {
private Bag bag;
public Audience(Bag bag){
this.bag = bag;
}
public Bag getBag() {
return bag;
}
}
Audience는 Bag을 가지고 있다.
아까 Theater의 enter코드는 이러한 관계를 생각하고 코딩이 되어있다.
이를 다르게 말하면 현재 객체 사이에 결합도가 매우 높다고 이야기 할 수 있을 것이다.
Audience와 Bag의 의존성이 너무 커지고 이에 대한 의존성이 Theater에서도 커지기 때문에 Audience에 Bag이 없어진다면 코드 전체를 수정해야하는 상황이 온다.
즉, 위의 코드는 가독성이 떨어지고 결합도가 높은 코드라고 이야기 할 수있을 것이다.
절차 지향적 설계 vs 객체 지향적 설계
맨 처음 작성한 코드는 Theater에서 enter라는 프로세스에 대해서만 집중을 했다.
이 때문에 Theater에 여러 책임이 몰리게 되었고 이에 따라 코드의 결합도가 높아지게 되었다.
이와 반대로 데이터와 프로세스가 동일한 모듈 내부에 위치하도록 프로그래밍하는 방식을 객체 지향 프로그래밍이라고 한다.
앞써 말한 프로세스를 전체적으로 관리하는 것이 아닌 각 모듈이 나눠 가지게 된다면 그에 따라 책임 또한 분산될 것이다.
또한 각 프로세스마다 필요한 데이터를 각 모듈이 가지게 되므로 다른 모듈에서는 다른 모듈의 데이터를 알 필요가 없어진다. 따라서 모듈화는 객체 사이의 결합도를 낮추게 된다.
따라서 우리가 변경에 유연하게 대응할 수 있는 코드를 개발하는 것이 목적이라면 절차 지향적 설계 보다는 객체 지향적 설계를 지향해야할 것이다.
아까 보았던 예시를 다시 보자.
- Ticket Seller의 티켓 판매
enter 부분을 자세히 보자.
ticket seller의 역할은 여기서 ticket office로 부터 ticket을 발행해주는 프로세스를 관리한다.
따라서 ticket seller의 역할을 theater에서 구현하는 것이 아니라 ticketseller내부에서 다음과 같이 구현되게 할 수 있을 것이다.
- Audience의 표를 사는 행위
ticketSeller의 sellto의 audience의 행위를 유심히 살펴보자.
audience은 표를 산다. 사실 초청장을 확인하는 행위 자체도 사실 audience가 해야하는 행위이다.
그렇기에 표를 산다는 행위의 책임을 Audience한테 국한시켜보자.
이렇게 되면 Audience는 자신이 초정장을 가지는지 유무에 따라 지불할 돈이 달리지고 이를 다음과 같이 TicketSeller가 반환 가격에 따라 처리하면 된다.
- 가방의 의인화
가방 또한 사실 티켓을 가지는지 말지는 audience의 책임으로도 볼 수 있지만 bag 자체의 책임이라고도 볼 수 있다.
따라서 다음과 같이 코드가 바뀔 수 있다.
가방이 ticket이 있는지 확인하고 ticket 가격을 알아서 audience한테 알려주는 것이다. - TicketOffice의 독립성 추가.
현재 코드는 ticketSeller가 ticketOffice의 티켓을 맘대로 가져간다. 즉, ticketOffice의 책임을 ticketSeller가 같이 가진다는 뜻이다. 따라서 코드를 다음과 같이 바꿔볼 수 있다.
다음과 같이 audience가 돈을 내고 티켓을 사고 돈을 정산하는것을 볼 수 있다.
또한 이렇게 된다면
ticketSeller가 판매하는 행위는 다음과 같이 요약 될 수 있다.
하지만 각 프로세스를 잘게 나누고 모듈화 하는 것이 좋은 객체지향적 코드라는 것은 아니다.
4번의 예시를 자세히 한번 더 봐보자. 바꾸기 전에는 ticketOffice가 audience를 잘 몰라도 된다. 하지만 코드를 수정후에는 ticketOffice와 audience가 의존성이 생긴다. 따라서 이런 부분은 TradeOff가 발생하게 되고 이는 비지니스 로직을 생각하면서 의존성을 어떻게 조절할 것인지 조절해야한다.
정리하자면 객체 지향의 세계에서는 객체들의 상호작용으로 어플리케이션이 구동된다.
따라서 객체 사이에 의존성이 생길 수 밖에 없고 이에 따라 객체지향적 설계에서 완전히 결합도를 낮추는 것은 불가능 하다.
따라서 객체 사이의 의존성을 적절하게 관리하는 것이 좋은 객체 지향적 설계의 요소가 될 것이다.
이 글은 오브젝트 책으로 부터 내용을 정리한 글입니다.
더 자세한 내용은 아래 오브젝트 책을 참고해주세요.
http://www.yes24.com/Product/Goods/74219491
긴 글 읽어주셔서 감사합니다.
틀린 부분이 있으면 댓글을 달아주시면 감사하겠습니다.
📧 : may3210@g.skku.edu
'Computer Science > 객체지향' 카테고리의 다른 글
[오브젝트] 역할 책임 협력 (0) | 2022.02.28 |
---|---|
[오브젝트] 객체지향 프로그래밍 (0) | 2022.02.22 |