이 영상은 (나한테 큰 충격과 깨달음을 주었던 객체지향 책인) 오브젝트의 저자님인 조영호 선생님께서 직접 설명해 주시는 객체지향 영상이다. 배달 플로우 중 "주문"을 기준으로 코드와 함께 설명해 주신다. 몇 달 전부터 보고 싶었으나 객체지향 개념이 부족해 이해하지 못하다가 이번에 다시 시청했다. 비록 뒷내용인 이벤트 핸들러 부분은 개념이 부족해 이해하지 못했지만 공부 후에 한 번 더 시청할 예정이다.
인상 깊은 내용을 위주로 정리하다 보니 잘못 작성된 부분이 있을 수 있습니다.
댓글로 알려주시면 감사하겠습니다.
의존성을 따라 시스템을 진화시켜라
의존성이란 무엇일까?
의존성이란 "변경에 의한 영향"이다. B가 변경될 때 A도 함께 변경되면 두 클래스는 의존 관계라고 본다. 하지만 설계가 잘 된다면 두 클래스가 영향을 주고받지 않는 관계를 형성할 수 있다.
💡 좋은 설계의 기준
- 변경에 초점을 맞추기
- 같이 변경되는 코드는 같이 넣고, 같이 변경되지 않는 코드는 따로 넣기
클래스 사이의 의존성
1. 연관관계
- 단순한 객체참조
- 영구적으로 갈 수 있는 경로
2. 의존관계
- 파라미터에 해당 타입이 나오거나, 리턴 타입으로 나오거나, 해당 타입을 생성
- 일시적으로 갈 수 있는 경로
3. 상속관계
- 구현이 바뀌면 영향을 받음
4. 실체화관계
- 인터페이스가 바뀌면 영향을 받음
패키지 사이의 의존성
- 클래스를 열었을 때 import 되어있으면 의존성이 있는 것
좋은 의존성을 관리하기 위한 규칙
- 양방향 의존성을 피하자
- A가 바뀔 때 B도 바뀌고, B가 바뀔 때 A도 바뀜
- 즉, 같은 클래스로 볼 수 있는 것을 두 클래스로 굳이 찢어놓은 것
- 신경 써야 할게 굉장히 많아짐
- 다중성이 적은 방향을 선택하라
- A가 컬렉션을 갖고 있고, B가 단일 객체를 갖고 있으면 B가 A를 참조하도록 하자
- 의존성이 필요 없다면 제거하라
- 당연히 가장 좋은 방안
- 패키지 사이의 의존성 사이클을 제거하라
- A → B → C와 같은 의존성 사이클이 발생하면 하나의 패키지로 봐야 됨
배달의 민족 주문 플로우로 예시 코드를 확인해 보자
화면 속 객체 상태
Q. 이 상태로 사용자는 장바구니 담았다. 여기서 메뉴 이름인 ‘1인 세트’가 ‘0.5인 세트’로 바뀌고 옵션의 이름인 ‘300g’이 ‘150g’으로 바뀌면 어떻게 되는가?
A. 사용자의 장바구니 데이터와 사장님의 메뉴 데이터 간의 불일치가 발생한다. 주문이 들어가면 데이터가 변경되기 전인 ‘1인 세트’로 주문이 들어간다. 즉, 이 과정에서 데이터 검증이 필요하다.
데이터 검증 과정
- 가게가 영업 중인지 확인
- 주문금액이 최소주문금액 이상인지
- 메뉴의 이름과 주문항목의 이름 비교
- 옵션그룹의 이름과 주문옵션그룹의 이름 비교
- 옵션의 이름과 주문옵션의 이름 비교
- 옵션의 가격과 주문옵션의 가격 비교
위 검증 과정을 통해 객체 상태를 확인하면 아래와 같은 상태를 얻을 수 있다.
즉, 가게를 중심으로 객체들의 상태가 퍼짐
협력 설계하기
- 주문하기()
- 영업여부확인하기() → 최소주문금액이상확인하기()
- 검증하기() → 검증하기(주문항목)
- 만족여부확인하기(주문옵션그룹) → 이름가져오기() → 만족여부확인하기(주문옵션) → 이름/가격가져오기()
위 플로우를 클래스 다이어그램으로 바꾸면 아래와 같다.
- 이 관계에서는 방향성이 필요하다. ex) 클래스와 클래스 사이의 관계
- 런타임에서 클래스의 인스턴스가 다른 클래스의 인스턴스와 어떤 협력이 이루어지는지. 메서드와 파라미터로 결정
관계의 방향 = 협력의 방향 = 의존성의 방향
- 연관관계
- 의존관계
- 상속관계
- 실체화관계
여기서 상속관계와 실체화관계는 명확하다. extends, implements에 따라 관계가 정의됨. 애매한 것은 연관관계와 의존관계.
연관관계
- 협력을 하기 위해 어떤 객체 사이에 영구적인 탐색 구조가 필요하다 → 연관관계로 연결
- Order가 Shop으로 가는 게 한 번 있으면 연관관계 필요 X. But 빈번하게 협력해야 하는 경우에 연관관계가 필요함
의존관계
- 메서드의 파라미터나 리턴타입 등으로 메시지를 넘겨받음
연관관계 = 탐색가능성?
- Order에서 OrderLineItem으로 탐색 가능
- Order가 뭔지 알면, Order를 통해 OrderLineItem을 찾을 수 있다.
- 두 객체 사이에 협력이 필요하고, 두 객체의 관계가 영구적이라면 연관관계를 이용하여 탐색 경로 구현
아래 코드로 이해할 수 있다.
- 객체 참조를 통해 연관관계를 형성하는 코드
Order → Shop & OrderLineItem 연관관계 연결
- Order는 Shop으로 이동해서 가게 영업여부를 확인해야 한다.
- Order는 OrderLineItem으로 이동해서 최소주문금액을 확인해야 한다.
가게의 영업여부를 확인하기 위해 Order가 Shop으로 메시지 전송
메뉴 이름 변동 여부를 확인하기 위해 OrderLineItem이 Menu로 메시지 전송
연관관계의 문제점
1. 어디까지 조회할 것인가?
- 참조로 연결되기 때문에 결합도가 높아진다.
- 어디까지 읽어야 하는지에 대한 가이드라인이 필요
2. 수정 시 도메인 규칙을 함께 적용할 경계는?
- 어떤 테이블에서 어떤 테이블까지 하나의 단위로 잠금을 설정해야 하는가
- 트랜잭션이 점점 길어지는 문제가 발생한다. → 성능 저하
객체 참조는 꼭 필요할까?
- 객체 참조는 결합도가 가장 높은 의존성
- 필요하지 않은 경우에는 객체 참조를 다 끊는 게 맞음
객체 참조가 필요한지 판단하는 규칙
무조건 분리하는 게 좋은 것은 아님.
- 함께 생성되고 함께 삭제되는 객체들
- 도메인 제약사항을 공유하는 객체들
→ 묶인 도메인들은 트랜잭션의 범위이자 조회의 경계가 된다.
But, 가능하면 분리하자! 객체를 참조하지 않고 id를 통해 레포지토리를 탐색할 수 있음
Q. Order와 Shop을 분리한 후 Shop을 id로 조회하면, 구현한 코드에서 참조하던 Shop 객체는 어떻게 되나요? (컴파일 에러)
A. 객체를 참조하는 로직을 다른 객체로 옮기면 된다. OrderValidator를 만들어 컴파일 에러가 발생하는 코드를 옮겨주면 된다. Validation Logic을 모음으로써 컴파일 에러를 해결할 수 있다. 또한 주문 처리 로직과 검증 로직이 분리됨으로써 응집도가 높아진다. 때로는 객체지향보다 절차지향이 필요하다.
결론
의존성을 이용해 설계 진화시키자
분리할 수 있는 객체들은 분리시켜 주고, 묶여야 할 객체들은 묶어주면서.
'IT' 카테고리의 다른 글
3-Tier Architecture란? (0) | 2023.09.18 |
---|---|
클래스 다이어그램으로 분석 단계 UML 작성해보기 (0) | 2023.08.21 |
책임과 협력 (0) | 2023.08.17 |