일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 기본 생성자
- 빈 스코프
- 가을야구
- 정적 컨텐츠
- Java Reflection API
- 의존성 주입
- 스프링 IoC 컨테이너
- 스프링
- 키움
- 컴포넌트 스캔
- 405 METHOD_NOT_ALLOWED
- entity
- jdbc template
- JPA
- RunWith
- 랜덤 포트
- SpringBootTest
- testresttemplate
- 통합 테스트
- 좋은 객체지향 설계 원칙
- jdbc
- Controller
- Not Acceptable
- 테스트 코드
- 406 NOT_ACCEPTABLE
- 스프링 데이터 JPA
- restTemplate
- 스프링 컨테이너
- 의존관계 자동 주입
- ResponseEntity
- Today
- Total
코드네임 JY
[오브젝트] 객체지향 설계 본문
🍿 소프트웨어 모듈이 가져야 하는 3가지 목적 (Robert C. Martin)
1️⃣ 실행 중에 제대로 동작해야 한다!
✔️ 너무 당연한 소리 🤪
2️⃣ 변경을 위해 존재해야 한다!
✔️ 하나의 클래스가 의존성이 너무 많다면? 코드를 변경하기 어렵다!
3️⃣ 코드를 읽는 사람과 의사소통할 수 있어야 한다!
✔️ 우리의 직관에 위배되지 않도록! 쉽게 읽고 이해할 수 있어야 한다!
✔️ 영화관 객체가 있고 손님 객체가 있는데, 영화관 객체가 손님 객체에 접근해서 마음대로 소지품이라는 항목을 뜯어본다면?
✔️ 소지품 항목은 손님 객체가 직접 다루는 것이 우리의 직관에 위배되지 않는다!
🍩 의존성 (Dependency)
의존성은 '변경' 과 관련되어 있다! 그래서 의존성은 '변경에 대한 영향' 을 나타낼 수 있다!
말을 더 풀어보면, "어떤 객체가 변경될 때, 그 객체에게 의존하는 다른 객체도 함께 변경될 수 있다" 는 사실이 내포되어 있다!
그래서 애플리케이션의 기능을 구현하는데 필요한 최소한의 의존성만 유지 하고, 불필요한 의존성은 제거 해야 한다!
객체 사이의 의존성이 과한 경우를 '결합도(Coupling)가 높다' 라고 말하는데, 결합도가 높을수록 코드를 변경하기 어려워진다!
객체가 최소한의 의존성으로 자신의 데이터를 자율적으로 스스로 처리한다면 '응집도(Cohesion)가 높다' 라고 말하는데,
응집도가 높은 객체는 밀접하게 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임한다!
🍔 캡슐화 (Encapsulation)
변경하기 쉬운 객체를 만들기 위해, 객체 내부로의 접근을 제한하면 객체 간의 결합도를 낮출 수 있다! 과한 의존성을 없앴다는 뜻!
이런 방식으로 객체 내부의 세부적인 사항을 감추는 것을 '캡슐화(Encapsulation)' 라고 한다!
🍟 절차지향 vs 객체지향
절차지향 | vs | 객체지향 |
프로세스와 데이터가 별도의 모듈에 위치 | 프로세스 & 데이터 |
프로세스와 데이터가 동일한 모듈 내부에 위치 |
모든 처리(프로세스)가 하나의 클래스 안에 위치 나머지 클래스는 데이터의 역할만 수행 |
자기 자신의 문제를 스스로 해결 | |
한 곳에 몰려 있음 | 제어 흐름 | 각 객체에 적절하게 분산되어 있음 하나의 기능을 완성하는데 필요한 책임이 여러 객체에 분산 |
우리의 직관에 위배, 데이터 변경에 대한 영향이 광범위, 코드 변경에 대한 두려움 |
특징 | 객체 내부의 변경이 외부로 파급되지 않도록 제어 가능, 변경이 수월함 |
간단하게 내용을 정리하면 위 표를 보면 된다! 이제부터 조금 자세히 풀어서 설명하겠다!
여기서 말하는 '프로세스' 는 데이터를 처리하는 로직이라고 생각하면 된다! 데이터는 말 그대로 데이터를 의미한다!
절차지향은 모든 처리가 하나의 클래스 안에 위치하고, 나머지 클래스는 단지 데이터의 역할만 수행할 뿐이다!
반면 객체지향은 데이터와, 데이터를 처리하는 로직이 동일한 공간에 있기 때문에, 자기 자신의 문제를 스스로 해결할 수 있다!
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();
Long ticketFee = ticket.getFee();
// 관객으로부터 돈이 나감
audience.getBag().minusAmount(ticketFee);
// 매표소로 돈이 들어옴
ticketSeller.getTicketOffice().plusAmount(ticketFee);
// 관객의 가방 안에 티켓을 넣어준다
audience.getBag().setTicket(ticket);
}
}
}
public class Audience {
private Bag bag;
public Audience(Bag bag) {
this.bag = bag;
}
public Bag getBag() {
return bag;
}
}
public class TicketSeller {
private TicketOffice ticketOffice;
public TicketSeller(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
public TicketOffice getTicketOffice() {
return ticketOffice;
}
}
위 코드는 절차지향 스타일의 코드다! 관객을 소극장에 입장시키는 로직(프로세스)을 전부 enter 메서드 안에서 처리한다.
그래서 Audience, TicketSeller 클래스는 단지 데이터에 불과한다! 제어 흐름이 하나의 메서드 안에 모두 몰려 있는 형태 이다.
public class Theater {
private TicketSeller ticketSeller;
public Theater(TicketSeller ticketSeller) {
this.ticketSeller = ticketSeller;
}
public void enter(Audience audience) { // 관람객을 소극장에 입장시키는 로직
ticketSeller.sellTo(audience);
}
}
public class Audience {
private Bag bag; // 가방을 지갑으로 변경하고 싶다면? Audience 클래스만 건드리면 된다!
public Audience(Bag bag) {
this.bag = bag;
}
public Long buy(Ticket ticket) {
return bag.hold(ticket);
}
}
public class TicketSeller {
private TicketOffice ticketOffice; // 매표소를 은행으로 변경하고 싶다면? TicketSeller 클래스만 건드리면 된다!
public TicketSeller(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
public void sellTo(Audience audience) {
ticketOffice.plusAmount(audience.buy(ticketOffice.getTicket()));
}
}
반면 위 코드는 객체지향 스타일의 코드이다. 각 클래스는 자신이 가지고 있는 데이터에 대한 처리를 할 수 있는 로직을 수행한다!
제어 흐름이 각 객체에 분산되어 있고, 다시 말해 하나의 기능을 완성하는데 필요한 책임이 여러 객체에 분산되어 있는 형태 이다!
핵심은? 객체지향적인 설계가 필요하다는 것..! 물론 한 번에 객체지향적인 설계를 뚝딱 만들어내는 것은 아직 쉽지 않다!! 🥲🥲
어느정도 큰 틀의 개념은 알았으니, 앞으로 코드를 짤 때 객체지향적인 설계를 할 수 있도록 계속 공부할 것이다!!
'백엔드 공부' 카테고리의 다른 글
[스프링 JPA] 엔티티 매핑 (0) | 2023.02.09 |
---|---|
[스프링 JPA] 영속성 컨텍스트 (0) | 2023.02.08 |
[DevOps] Github Actions CI/CD (0) | 2023.01.27 |
[DevOps] 빌드와 배포, CI/CD (0) | 2023.01.25 |
[CRUD 연습] CRUD 기능 구현 (0) | 2023.01.13 |