✏️코드
✅ 모델을 먼저 설계하자!
1주차의 프리코스를 마치고 회고하는 과정에서 가장 큰 깨달음은
" 객체지향적 사고, 객체 간의 상호작용 "이다.
따라서 객체를 어떻게 구성하고 책임을 분배할 것인지는 OOP에서 매우 핵심적인 요소를 차지한다.
그래서 이번에 미션을 진행할 때 모델을 중심으로 우선 설계하기로 하였다.
문제를 받자마자 생각의 회로는 다음과 같았다.
- 🤔 자동차의 name과 position을 관리하는 객체가 필요하겠다! => Car
- 🤔 자동차에 대해 여러 개의 입력을 받을 수 있으므로 자동차를 리스트로 관리하는 Cars 객체가 있으면 좋겠다!
이는 자연스럽게 일급 컬렉션이 구상되었고 다음과 같은 구조를 지니게 되었다.
☑️일급컬렉션의 활용!
Cars의 객체를 일급컬렉션으로 구상함으로써 Car에 대해 리스트로 관리할 수 있다.
이로 인해, Car 객체에서는 name에 대한 validate만 진행할 수 있으므로 Car 객체의 내부상태에 대한 캡슐화된다.
또한 Cars 객체가 컬렉션 전체의 상태를 관리함으로써 컬렉션의 일관성을 유지할 수 있다!
이렇게 Car와 Cars에 대한 큰 모델의 뼈를 잡아놓으니 이후에 추가되는 기능들에 대해서는 쉽게 더해갈 수 있었다.
☑️ 원시값 포장으로 모델에 대한 부담을 줄여주자.
Car객체에 살을 붙여 나가다 보니, Car는 position과 name을 관리하는 객체가 되어버렸다.
로직 상에는 아무런 이상 없지만,
- name과 position을 관리하게 되면서 부담이 커져버렸다.
- position에 대해서도 검증해야 하는 로직이 추가된다면 가독성이 떨어질 것이다.
라는 이유로 name을 CarName 클래스로 포장하면 어떨까에 대한 접근을 시도했다!
이렇게 name에 대한 필드를 CarName 클래스로 포장함으로써
CarName 클래스는 name에 대한 검증 로직에만 집중할 수 있고,
Car 객체는 name에 대한 검증을 따로 거치지 않아도 가능해졌다!
이렇게 함으로써 다음과 같은 이점을 챙길 수 있게 되었다.
- 🔍검증 로직의 재사용성 증가
CarName 클래스로 분리하면, 이름 검증 로직이 한 곳에 모이게 되며, 다른 곳에서 재사용하기 더 쉬워진다. - 🔍 응집도 향상 및 책임의 분리
원시 값을 포장하면, 해당 원시 값과 관련된 검증 및 처리 로직이 모두 CarName 클래스로 이동하게 된다.
이를 통해 Car 클래스는 자동차 객체 자체의 로직에 집중할 수 있으며, 응집도가 높아지고 책임이 분리된다. - 🔍 불변성 및 안정성
CarName 클래스로 포장하면, CarName 객체를 불변 객체로 만들 수 있다.
즉, 한 번 생성된 이름이 변경되지 않도록 할 수 있으며, 안전하고 신뢰성 있는 코드를 작성할 수 있다
(물론 더 나아가서 position에 대해서도 원시값 포장이 가능하겠지만, 현재는 position에 대한 검증을 할 필요가 없으므로 그대로 두었다 [1주 차 회고 과정 중 YAGNI 법칙을 반영] )
✅ Trial에 대한 검증?? , Validate 로직은 모델에서! (되도록이면?)
validate 로직은 모델에서 진행해야 함을 한 번 더 느꼈던 과제였다.
처음에는 위의 코드와 같이 rawTrialCount를 컨트롤러에서 입력받아 Integer로 변환하는 작업을 진행하였다.
그런데, 이렇게 진행하는 경우에는 다음과 같은 문제점이 발생하였다.
- 🤔 예외 메시지도 컨트롤러에서 처리할 것인가? => controller의 부담 증가
- 🤔 만약, trialCount에 또 다른 검증 조건이 추가되는 경우(ex: 10회 미만)에는 어떻게 할 것인가?
이러한 문제점에 대해 생각한 해결 방안은 두 가지가 있다.
- 🤓 validate를 처리하는 parser를 따로 만들자!
- 🤓 trialCount에 대한 객체를 생성하고 객체 내부에서 validate 로직을 처리하자!
그러나 첫 번째 해결 방안 같은 경우는 validate Parser를 util 레이어에서 구현하고,
이러한 util 역할을 하는 메서드들은 의존적이면 구조가 복잡해지므로 static으로 선언하게 된다.
그렇다면 과연 이러한 util 메서드는 객체지향적이라고 할 수 있을까?
그러면 두 번째 해결 방안에 대해 생각해 보자.
두 번째 해결 방안은 다음과 같은 이점이 있다.
- 💡검증 코드를 생성자에 넣어두면, 유효하지 않은 객체가 생성되는 것을 막을 수 있다.
- 💡검증 코드를 한 곳에서 관리할 수 있으므로 유지보수성도 뛰어나다!
따라서 GameRound(TrialCount) 모델을 만들어서 내부에서 검증을 시도하기로 하였다.
이렇게 함으로써 들어온 값이 양수인지, 숫자로 들어오는지에 대한 검증을 하나의 객체에서 처리할 수 있게 되었다!
(물론 "무조건 모델에서만" validation을 처리하는 것은 독이 될 수도 있는 사고이므로 유연한 사고가 필요할 것 같다)
따라서 다음과 같은 구조가 완성되었다.
✅ RandomNumberGenerator 분기해야 할까? - util의 등장!
차수별 실행 결과를 로직 중에 랜덤 수가 4 이상인 경우 car가 움직여야 한다는 로직이 있어야 한다.
이렇게 Cars 객체 내부에서 코드를 짜는 중 문득 다음과 같은 생각이 들었다.
🤔 "랜덤수를 생성하는 것은 일종의 하나의 책임으로써 분리해도 되지 않을까?"
그래서 RandomGenerator를 따로 객체로 뺀다면 util 클래스에서 static으로 활용할 것이고,
이렇게 되었을 때의 이점에 대해 생각해 보았다.
- 🤓단일 책임 원칙 준수
Cars 객체는 이제 자동차의 움직임과 관련된 로직에 집중할 수 있으며, 랜덤 수 생성과 같은 세부적인 구현은 RandomGenerator가 담당하게 된다.
이를 통해 각 클래스가 명확한 책임을 가지게 되고, 코드의 응집도가 높아진다. - 🤓 재사용성 증가
RandomGenerator 클래스가 별도로 존재함으로써, 프로젝트의 다른 부분에서도 동일한 랜덤 수 생성 로직을 일관되게 사용할 수 있다.
필요에 따라 다양한 랜덤 생성 전략을 추가하거나 수정할 때, 해당 클래스를 수정하기만 하면 된다. - 🤓 테스트 용이성
RandomGenerator가 별도로 분리되어 있기 때문에, 랜덤 수 생성과 관련된 로직을 독립적으로 테스트할 수 있다.
또한, 단위 테스트에서 Cars 객체의 움직임 조건을 테스트할 때, 의존성을 쉽게 모킹(Mocking)할 수 있어 테스트의 복잡성이 감소한다. - 🤓 코드 유지보수성 향상
랜덤 수 생성 로직이 특정 클래스에 하드코딩되지 않고, 독립된 유틸리티 클래스로 관리되기 때문에 코드의 수정 및 유지보수가 용이해진다. 만약 랜덤 생성 로직에 변경이 필요할 경우, RandomGenerator 클래스만 수정하면 된다.
만약 4 이상이 아닌 5, 6, 7 등등 다른 숫자가 들어왔을 때 유연하게 대처가 가능해진다!
따라서, 이러한 이유들을 가지고 분리하기로 결심하였다!
이렇게 하여 최종적인 구조는 다음과 같다!
👀 고민되는 부분
View에서는 View는 자기 자신 이외에 아무도 알아서는 안된다고 알고 있다.
그러나 내가 구현한 로직에서는 뷰로 모델을 넘기고
모델의 내부 메서드를 이용하여 outputView의 메서드를 구성하고 있다.
이렇게 구성하면 view가 model을 알게 되는 것일까?
그렇다면 더 좋은 설계는 Controller에서 변수를 만들어 Cars.getWinner의 값을 받아오고
변수를 view에게 넘겨주는 게 더 좋은 설계 방식인 걸까?
지금은 기능이 단순하여 간단한 회로이지만,
만약에 이러한 과정이 복잡해진다면 그때는 Service의 레이어를 도입할 이유가 되는 것인가?
(1주 차 때의 고민과도 직결된다..)
'JAVA > JAVA' 카테고리의 다른 글
[우테코 프리코스 2주차] java-racingcar [🤔회고] (캡슐화, view에 데이터 넘기기, 무분별한 상수 처리) (0) | 2024.11.04 |
---|---|
[우테코 프리코스 1주차] java-calculator [🤔회고] (OOP, 일급컬렉션, YAGNI의 법칙) (2) | 2024.10.27 |
[우테코 프리코스 1주차] java-calculator 코드설명 [💻문제 해결] (정규 표현식, 이스케이프 문자) (1) | 2024.10.21 |
[우테코 프리코스 1주차] java-calculator 코드설명 [🛠️설계] (MVC 패턴 적용, OCP, SRP) (0) | 2024.10.19 |
[JAVA] MVC 패턴이란? (개념, 설계 원칙, 사용하는 이유) (0) | 2024.09.24 |