지난 과제를 돌아보며 생각한 것 (with 객체지향)(1) mvc의 필요성(2) 객체지향 vs 절차지향(함수지향)(3) 연관관계 vs 의존관계[강연] ‘의존성을 이용해 설계 진화시키기’를 듣고Today in 프리코스
지난 과제를 돌아보며 생각한 것 (with 객체지향)
(1) mvc의 필요성
- 지난 과제에서 mvc를 적용하려 한 사람들이라면 누구나 공감할 만한 부분이 있다. 바로, 사용자로부터 3자리 숫자를 입력하는 부분을 view에서 하기 까다롭단 것이다. 사실 이번 과제에선 mvc를 쓰지 않고 3자리 숫자 입력을 특정 모델이 책임지게 했으면 좀 더 객체지향적이며 자연스레 메서드도 작은 단위로 추출될 수 있었을 거 같다.
- 무슨 말이냐면, "숫자를 입력해주세요" 부분은 Player의 행위를 위한 객체에 속하면 더 좋았을 거 같다. 대신 이렇게 되면 mvc 개념을 넣긴 힘들어질것이다. 하지만, mvc란게 애초에 사람들이 구현을 하다 어떤 임계점에서 특정 패턴을 정리한 것뿐이지 무조건 써야 하는 것이 아니기에, 돌이켜보면 1주차 과제는 그 임계점을 넘지 않는 과제가 아니였을까 싶다.
(2) 객체지향 vs 절차지향(함수지향)
- 객치지향과 절차지향은 반댓말이 아니다. 그 이유는 절차지향을 하다가 발전된 게 객체지향이기 때문이다. 따라서 객체지향적인 코드가 절차지향적인 특성을 아예 안 갖는 건 아니다. 객체지향 역시 절차적으로 실행된다. 그렇다면 뭐가 다른 걸까? 이 실행의 목적이 다르다. 절차지향은 오직 순차적으로 실행되는 데에 목적을 두고 있고, 객체지향은 객체 간의 관계가 실행되는 데에 목적을 두고 있다.
- 좀 더 직관적으로 말해보자면, 절차지향은 데이터를 중심으로 함수를 구현하는 거고 객체지향은 기능을 중심으로 함수를 구현한다. 그렇다면 어떻게 해야 데이터 중심으로 함수를 구현하지 않고 기능 중심으로 함수를 구현할 수 있을까? 바로 데이터 위주의 사고가 아닌 행동 위주의 사고를 하는 것이다.
- 데이터 위주의 사고는 예를 들면 “자동차는 바퀴, 프레임, 엔진, 방향, 속도, 값들을 갖고 있어야겠다”라고 생각한 후
Car { private List<Wheel> wheels; private Speed speed; }와 같은 식으로 코드를 짜는 것이다.
- 반면, 행동 위주의 사고는 “자동차는 달릴 수 있어야 하고, 속도 조절할 수 있어야 하고, 방향도 바뀔 수 있어야겠다.”라고 생각한 후
Car { public void drive(); public void accelerate(Speed speed) }와 같은 식으로 코드를 짜는 것이다.
- 사실 전자의 결과물은 struct를 만든 거고 후자의 결과물이 class를 만든 것이다. 행동 위주의 사고를 하는 게 객체지향적인 코드를 짜게 될 확률이 올라간다.
내 코드
InputValidator: 클래스의 상태를 갖지 않는 유틸리티 클래스인데, 유틸리티 클래스 사용은 객체지향적이지 않은 패턴 중 하나다. 이 책임을 Player와 같은 다른 클래스로 이동시키는 것이 좋다.
Round: 객체 생성 시마다 랜덤한 번호를 생성하는 클래스인데, 이렇게 클래스 내부에서 직접 랜덤 번호를 생성하는 건 절차지향적인 특성이다. RandomNumberGenerator 같은 클래스를 사용해 Round에 랜덤 번호를 주입해 주는 것이 좋다.
BaseballGameController: 게임을 시작하는 책임 뿐만 아니라 여러 라운드를 관리하는 책임도 가지고 있다. 라운드 관리는 별도 클래스로 분리해 각 클래스의 책임 더 명확히 할 수 있다.
(3) 연관관계 vs 의존관계
- 지난 과제에서 모델 간 의존성을 떨어트리고 싶었는데 사실 모델끼리 그 어떤 관계도 맺지 않는 건 어려운 일이었다. 특정 모델이 다른 모델을 attribute로 가져야 할 수도 있고, 다른 모델을 사용해야 할 수도 있다. 그렇다면 어디까지가 의존성을 높이지 않는 선에서 허용되는 건지 궁금해졌었다.
- 이에 대해 어느 정도 해답을 준 게
연관관계와 의존관계라는 키워드였다. 연관관계는 우리가 알고 있는 has a 관계다.Car { private Speed speed; }와 같이 Car가 Speed를 변수로 갖는 Association 관계이다. 의존관계를 사실 잘 몰랐는데 이는Car { public void accelerate(Speed speed) { return new Speed(); }와 같이 Car의 매개변수 혹은 반환타입으로 Speed를 사용하는 것이다. 이 경우 Car가 Speed에 의존적이라고 한다.
- 그렇다면 둘 중 모델 간 의존성을 높이는 것은 무엇일까? 이건 아래 강연를 보고 더 고민해보았다. 결론은 설계에 따라 다르다. 의존성이란건 특정 클래스가 변경될 때 다른 클래스도 변경될 가능성이 있단 걸 의미하는데, 의존성이 있어도 설계를 잘하면 변경으로 인한 파장 효과를 없앨 수도 있다. 이를 위한 기법으로 강의에선 event handler를 이야기 하는데 얼핏 저게 ddd에서 나오는 용어란 걸 알 수 있었다.
- 사실 나는 아직 객체지향도 완벽히 이해하지 못했는데 ddd까지 욕심 부리는 건 무리라고 판단했다. ddd가 정확히 뭔지도 잘 모르고 말이다.. 따라서 이번 2주차 과제에선 어쩌면 프리코스 기간 동안엔 의존성 결합에 대해 크게 집착하지 않기로 했다.
참고
[강연] ‘의존성을 이용해 설계 진화시키기’를 듣고
- 아래 강연을 듣고 정리하며 든 생각들은 (🐱)으로 표시했습니다.
- 설계 : 코드를 어떻게 배치할 것인지에 대한 의사 결정
- 같이 변경되는 코드를 같이 넣고, 같이 변경되지 않으면 따로 넣자
- 의존성 : A가 B에 의존(A→B)한단건 B가 변경될 때 A도 같이 변경될 수 있음(가능성)을 의미함
- 변경은 클래스 이름, 메서드 이름, 구현 등 뭐든 될 수 있음
- 의존성이 있다고 해서 무조건 변경되는 건 아님. 설계 잘하면 B가 변경되더라도 A에 영향 주지 않을 수도 있음
- 클래스 의존성
- 연관관계 : A에서 B로 이동할 수 있음 (영구적 경로)
- 의존관계 : A와 B가 협력하는 시점에 관계맺고 헤어짐 (🐱 의존성 아님ㅎ)
- 상속관계 : B의 구현만 바뀌어도 영향 받음
- 실체화관계 : B의 인터페이스 시그니처 바뀌었을 때만 영향 받음
- 패키지 의존성
- 패키지에 포함된 클래스 사이의 의존성
- 클래스 열었을 때 import에 다른 패키지 이름 있으면 의존성 있는 거임
- 의존성 관리 규칙 : 설계 시 가이드
- 가급적이면 양방향 의존성을 피하라
- 양방향 의존성을 가질 때 사실 그 두 클래스는 하나의 클래스여도 무방한 데 억지로 찢은 게 아닐지 의심해보기
- 양방향은 동시성 관리하기 빡셈
- 다중성이 적은 방향을 선택하라
- 일대다 말고 다대일로 표현하기
- 일대다의 경우 List, Collection, Set 같은 걸 변수로 가져야 할텐데 성능 이슈 관리나 객체들 관계 유지가 빡셈
- 되도록이면 의존성이 필요없다면 제거하라
- 패키지 사이의 의존성 사이클을 제거하라
- 패키지 사이엔 양방향 의존성이 있으면 안됨
- 예제 : 여러 객체에 분산된 validation logic 분리
- validation 로직과 주문 처리 로직은 변경 시점이 다를 거임
- 따라서 Order 객체에서 validation 로직은 떼어냈음
- 때로는 절차지향이 객체지향보다 좋다. 객체 안에 validation 로직 다 넣을 필요 없다.
- 상태 조금 체크하는 거면 객체 안에 넣는 게 나은데, 이를 체크하기 위해 여러 객체를 사용해야 한다면 떼어내는 게 나을 수 있다.
- 이가 객체의 결합도는 높이지만 응집도는 낮출 수 있다.
- 예제 : 도메인 로직의 순차적 실행으로 인한 도메인 간 높은 결합도 이슈 해결
- 첫 번째 방법 : 절차지향적으로 로직 모으기 → 비즈니스 플로우 한 눈에 볼 수 있음
- 이때 사이클 생기면 → 인터페이스 사용해서 의존성 역전시키기
- 두 번째 방법 : 도메인 이벤트 퍼블리싱
Today in 프리코스
TIL 작성하기
준비
메일 꼼꼼히 읽고 정리하기
몰입
지난 과제 돌아보며 생각했던 점들 전부 정리하며 확장시키기
우아한 객체지향 강연에서 설계 부분 보기
1일차_둘러보기, 환경설정하기
Oct 19, 2023
DIARY_DEVELOP
2일차_컨벤션 정리하기
Oct 20, 2023
DIARY_DEVELOP
3일차_설계에 대해 고민하기
Oct 21, 2023
DIARY_DEVELOP
4,5일차_MVC 온전히 이해하기
Oct 22, 2023
DIARY_DEVELOP
6일차_설계를 코드로 구현하기
Oct 24, 2023
DIARY_DEVELOP
7일차_리팩토링과 마무리하기
Oct 25, 2023
DIARY_DEVELOP
8,9일차_코드 리뷰 통해 객체지향에 다가가기
Oct 27, 2023
DIARY_DEVELOP
10일차_지난 과제 돌아보며 객체지향 이해하기, 의존성과 설계의 관계 맛보기
Oct 28, 2023
DIARY_DEVELOP
11일차_객체지향을 미션 설계에 적용하기(with [책] 객체 지향의 사실과 오해)
Oct 29, 2023
DIARY_DEVELOP
12일차_기능 별로 구현하며 단위 테스트의 필요성 느끼기 (with [책]
자바와 JUnit을 활용한 실용주의 단위 테스트)Oct 30, 2023
DIARY_DEVELOP
13,14일차_일급 컬렉션과 레코드 적용해 리팩토링하기
Oct 31, 2023
DIARY_DEVELOP
15, 16일차_코드 리뷰를 통해 성장하기(1)_다른 사람의 코드 읽으면 배운 것 정리
Nov 2, 2023
DIARY_DEVELOP
17일차_코드 리뷰를 통해 성장하기(2)_내 코드 개선하며 배운 것 정리
Nov 4, 2023
DIARY_DEVELOP
18일차_내가 찾은 설계 방법 공유하기, 공유에 대해 고민하기
Nov 5, 2023
DIARY_DEVELOP
19일차_지난 과제 피드백 고려해 설계하기
Nov 6, 2023
DIARY_DEVELOP
20, 21일차_일단 돌아가는 코드를 만들기
Nov 7, 2023
DIARY_DEVELOP
22일차_현재 도움이 될 것 생각하기 (디자인패턴과 mvc2 과감히 패스)
Nov 9, 2023
DIARY_DEVELOP
23일차_코드리뷰하기 (feat. converter 파고들기)
Nov 10, 2023
DIARY_DEVELOP
24일차_기획을 문서화하기
Nov 11, 2023
DIARY_DEVELOP
25일차_설계하며 고민하기
Nov 12, 2023
DIARY_DEVELOP
26, 27일차_구현하며 고민하기
Nov 13, 2023
DIARY_DEVELOP

![[우아한테크세미나] 190620 우아한객체지향 by 우아한형제들 개발실장 조영호님](https://www.notion.so/image/https%3A%2F%2Fi.ytimg.com%2Fvi%2FdJ5C4qRqAgA%2Fmaxresdefault.jpg?table=block&id=c2550edb-a4fe-4801-b6e1-7eeb778c99aa&cache=v2)