일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 메시지
- 네트워크
- 이펙티브 자바
- restControllerAdvice
- 커스텀예외
- 자바
- Effective Java
- AWS
- 프로그래머스
- 토이프로젝트
- HandlerExceptionResolver
- docker
- 스프링부트
- CI/CD
- kotlinglogging
- JIRA
- 도커
- githubactions
- http 완벽가이드
- http
- 스프링
- 벨먼-포드
- 국제화
- java
- 데이터베이스
- 백준
- Spring
- 트랜잭션
- yaml-resource-bundle
- 파이썬
- Today
- Total
땃쥐네
[OOP] 오브젝트 - 1.2 무엇이 문제인가 본문
이 글은
- 조영호 님의 '오브젝트' 1장 객체, 설계의 2절 "무엇이 문제인가" 부분을 정리하면서 다루고 있다.
- 1.1절에 관한 글은 이전 글을 참고
- 오브젝트는 전반적으로 객체지향 개념을 직접 코드로 작성해서 설명하는데, 책만 읽지 말고 직접 예제 코드를 코드로 작성하여 실습해보는 것을 권장한다.
모든 모듈은 제대로 실행돼야 하고, 변경이 용이해야 하며, 이해하기 쉬워야한다.
1. 수동적인 객체 → 읽기 어려운 코드, 예상을 벗어나는 코드
public class Theater {
private TicketSeller ticketSeller;
public Theater(TicketSeller ticketSeller) {
this.ticketSeller = ticketSeller;
}
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);
}
}
}
실세계에서도 개개인이 자기 자신의 일을 스스로 처리하지 못 하는 것은 우리의 직관을 벗어난다.
이 코드가 말하고자 하는 의도를 정확하게 의사소통하지 못 하기 때문에 이해하기 어려운 코드가 됐다.
1.1 읽기 어려운 코드, 예상을 벗어나는 코드
- 읽기 어려운 코드, 예상을 벗어나는 코드
- 제대로 실행은 되지만, 변경이 쉽지 않고, 이해하기 어렵다.
전반적으로 위의 코드를 봤을 때 1차적으로 드는 생각은 코드가 읽기 어렵고 인간이 생각하는 실세계의 관점과 어긋난다는 것이다.
1.2 수동적인 객체
- Audience, TicketSeller가 Theater의 통제를 받는 수동적인 존재
- 코드의 맥락을 읽어보면 우리의 상식과 너무 다르게 동작하기 때문에 코드를 읽는 다른 사람과 제대로 의사소통하기 어렵다.
코드의 맥락을 읽어보면 Theater가 ticketSeller의 가방을 뒤지고 안에 있는 bag이 Invitation을 갖고 있는 지 판단하고 이를 기반으로 수행해야할 일을 if문으로 분기처리한다.
단순히 Theater가 getter를 통해 객체의 프로퍼티를 꺼내와서 판단하고, 판단 결과를 기반하여 객체 내부의 상태를 외부의 Theater가 setter로 수정하고 있다. 이는 실세계의 인간관점에서 봤을 때 부자연스럽고 코드 한 줄 한 줄을 읽기 힘들다. 객 객체는 그저 데이터 덩어리일 뿐이다.
1.3 하나의 클래스, 메서드에서 너무 많은 세부사항을 다룸
- 하나의 클래스, 메서드에서 너무 많은 세부사항을 다루기 때문에 코드 작성자 뿐 아니라 코드를 이해해야하는 사람 모두에게 큰 부담을 줌
- getter로 다른 클래스의 세세한 부분까지 접근하여 간섭을 하고 있음. 너무 많은 것을 알고 있다.
이 코드에서 Theater는 Audience가 Bag을 가지고 있다는 사실, Bag에는 Invitation이 있을 수도 있고 없을 수도 있다는 사실 TicketSeller가 TicketOffice를 알고 있는 사실, TicketOffice를 통해서 티켓을 얻어올 수 있다는 사실, ... 등등 많은 것을 알고 있고 이에 간섭하고 있다.
코드를 작성하는 사람은 해당 객체들에 대해서 지식을 알고 있어야하고 이를 기반으로 코드를 작성해야한다.
코드를 읽는 입장에서도 이 각각의 객체들에 대한 지식을 필요로 한다.
양쪽 모두에게 큰 부담이다.
2. 불필요한 의존성 → 높은 결합도 → 변경에 취약한 코드
불필요한 의존성이 발생하여 결합도가 높아지고, 변경의 파급효과가 커짐으로서 변경하기 어려운 코드가 됐다.
2.1 변경에 취약하다
- 이 코드에서 Theater는 TicketSeller가 근무하고 있는 TicketOffice도 알고 있고, Audience가 Bag을 갖고 있는 사실, Bag의 상태 등을 참조하고 있다.
- Theater의 코드에 참여하고 있는 Audience, Bag 클래스의 구현 코드가 변경하면 해당 클래스뿐만 아니라
Theater의 코드도 변경해야한다.
- 이렇게 클라이언트가 참조하고 있는 객체의 내부에 대해 더 많이 알면 알 수록, 코드를 더 변경하기 어려워진다. 참조되는 객체가 변경되면 그 파급효과가 이 객체를 참조하는 다른 객체들에게도 전파되기 때문이다.
2.2 불필요한 의존성(Dependency)은 변경에 대한 영향을 암시한다.
- 의존성 : 어떤 객체가 다른 객체를 알고 있는 것.
- 내부에 대한 더 많은 노출, 그리고 이러한 내부 구현사항에 대한 지나친 의존은 변경하기 어려운 코드를 만든다.
- 의존성은 변경에 대한 영향을 암시한다. 암시 어떤 객체가 변경될 때 이를 의존하는 다른 객체도 함께 변경될 수 있다.
2.3 결합도(coupling)
- 객체 사이의 의존성에 대한 정도를 칭하는 말
- 의존성이 과하다 = 결합도가 높다.
- 객체들이 합리적으로 의존한다 = 결합도가 낮다.
3. 불필요한 의존성을 최소하하여 결합도를 낮춰라.
- 의존성을 완전히 없애는 것이 정답은 아니다. 결국 만들고자 하는 것은 협력하는 객체들의 공동체를 구축하는 것이므로 의존성은 필요하다.
- 현실적인 목표로, 애플리케이션의 기능을 구현하는 데 필요한 최소한의 의존성만 유지하고 불필요한 의존성을 제거하는 것을 두자.
- 객체 사이의 결합도를 낮춰 변경이 용이한 설계를 만드는 것을 목표로 두자.
핵심 포인트
- getter setter를 통해 객체의 내부에 대해 외부 객체가 너무 많은 간섭을 하여 객체는 그저 데이터 덩어리일 뿐인 수동적인 존재가 됐다. 최종적인 구현 결과물도 읽기 힘들어 졌다.
- 불필요한 의존성이 높은 결합도의 코드를 만들고 코드를 변경하기 힘들게 만든다.
- 최소한의 의존성만 유지하고 불필요한 의존성을 제거하자.
'Design > OOP' 카테고리의 다른 글
[OOP] 오브젝트 - 1.1 티켓 판매 애플리케이션 구현하기 (0) | 2022.12.18 |
---|