우테코 프리코스가 끝나고...
평화가 찾아온 정윤's 자바 마을에는.. (물론 아직 학기 중인 게 흠이지만)
리팩토링 열풍이 부는데...
3주차 로또를 리팩토링하며 4주 차 구현에 바빠 제대로 반영 못한 3주 차 피드백을 펼쳐 보는데..
피드백 한 번 보고
내 코드 한 번 보고
뜨아익.. 순살 공격
그때 생각난 과거.. 어릴 때 로마 여행을 간 적이 있는데 그때 박물관이나 미술관에 가는 길에
가이드 분이 아는 만큼 작품이 보일 거라며 역사(?)를
엄청 재밌게 들려주셨다. 그리고 정말로 알고 보니 유적지나 미술관 갈 때 설명이 떠오르면서 보이더라..
그렇다.. 아는만큼 보인다.
알고 보니 눈물난다. 이 코드 과거의 너? 누가 쓴 거냐
와 서론이 길다. 시작해보자. 리팩토링
오늘 리팩토링할 내용은 ' [우아한테크코스] 프리코스 3주차 : 로또 (lotto) ' 이다.
1. 객체를 객체스럽게
흔히 객체를 상태(state)와 행동(behavior)을 함께 지닌 설계라고 정의한다. 이 말은 객체가 협력에 참여하기 위해 어떤 행동을 해야 한다면 그 행동을 하는 데 필요한 상태도 지니고 있어야 한다는 것을 의미한다.
커피를 제조하는 바리스타가 제조 방법을 모른다는 것이 말이 되지 않는 것처럼 객체가 어떤 행동을 하기 위해 필요한 상태를 알지 못한다는 것 역시 말이 되지 않는다.
객체가 협력에 참여하는 과정 속에서 스스로 결정하는 자율적인 존재로 남기 위해서는 필요한 행동과 상태를 함께 지니고 있어야 한다.
- 객체지향의 사실과 오해 (p32-33)
객체는 협력적이면서 자율적이다. 그래서 객체의 사적인 부분은 객체 스스로 관리하고 외부에서 일체 간섭할 수 없어야 한다.
- 허락된 수단을 통해서만 의사소통
- 무엇(what)을 수행하는지 알 수 있지만 어떻게(how) 수행하는지는 알 수 없다.
✔️ 결론: 객체는 상태와 행위를 하나의 단위로 묶는 자율적인 존재
따라서 상태와 행위는 한 곳에서 관리해야 한다.
이것이 객체이다.
Lotto 클래스는 numbers를 상태 값으로 가지는 객체이다. 그런데 이 객체는 로직에 대한 구현은 하나도 없고, numbers에 대한 getter 메서드만을 가진다.
이때 객체는 상태(private으로)를 캡슐 안에 감춰 외부에 노출시키지 않는다. 그리고 외부에서 접근하는 방법은 행동(getter로) 뿐이다.
나는 Lotto에 로또 번호의 상태를 저장해뒀으면서 이걸 getter로 꺼내서 외부에서 비교했다. 부자연스럽다. 객체에서 numbers를 꺼내지 말고 객체에 메세지를 던져서 확인하자.
리팩토링 원칙
- 상태를 가지는 객체를 추가했다면 객체가 제대로 된 역할을 하도록 구현해야 한다.
- 객체가 로직을 구현하도록 해야한다.
- 상태 데이터를 꺼내 로직을 처리하도록 구현하지 말고 객체에 메시지를 보내 일을 하도록 리팩토링한다.
2. 책임-주도 설계
크게 보면 Lotto 미션에서 LottoGame을 진행하는 LottoGame과 로또 번호 상태를 저장하는 Lotto 객체, 로또 게임의 결과를 저장하는 LottoResult 객체가 있다.
이때 구매한 여러개 로또 번호를 리스트로 가지고 있는 객체도 필요하다고 생각했다.
그래서 Lottos 객체도 하나 더 생성했다.
그런데 이런 설계를 할 때 어떻게 객체를 구성할까. 그 기준은 어떤 상태를 가진 객체가 필요하냐 일까?
알아보자!
책임 주도 설계
행동이 상태를 결정한다.
- 객체지향의 사실과 오해 (p64-33)
방금 위에서 Lottos 객체를 추가하면서 나는 Lottos에 어떤 상태를 저장할 지 부터 생각했다.
그런데 이 것은 설계에 나쁜 영향을 미친다.
- 캡슐화가 저해된다.
- 객체 내부로 깔끔하게 캡슐화되지 못하고 노출되어버릴 확률 높다.
- 객체를 협력자가 아닌 고립된 섬으로 만든다.
- 객체가 필요한 이유는 애플리케이션의 문맥 내에 다른 객체와 협력하기 위한 것임을 기억하자.
- 재사용성이 저하된다.
- 상태에 초점을 맞춘 객체는 다양한 협력에 참여하지 어렵다.
✔️ 어떤 책임이 필요한가를 결정하는 과정이 전체 설계를 주도해야한다. -> 책임-주도 설계
✔️ 행동이 상태를 결정한다.
3. 일급 컬렉션 (First Class Collection)
다음과 같이 되어 있는 것을
List<Lotto> purchaseLotto = buyLotto(quantity);
아래와 같이 Wrapping하여 일급 컬렉션으로 만들었다.
public class Lottos {
private final List<Lotto> purchaseLotto;
public Lottos(int quantity) {
this.purchaseLotto = buyLotto(quantity);
}
}
Lottos lottos = new Lottos(quantity);
4. 리팩토링
📂 수정한 폴더 구조
├─main
│ └─java
│ └─lotto
│ │ Application.java - 로또 게임 애플리케이션 실행
│ │
│ ├─controller
│ │ LottoGame.java - domain-view를 연결하여 게임 순서 결정
│ │
│ ├─domain
│ │ GenerateNumbers.java - 랜덤 6개 숫자 리스트를 만드는 객체의 인터페이스
│ │ GenerateRandomNumbersImpl.java - 랜덤 6개 숫자 리스트를 만드는 객체 (구현체)
│ │ Lotto.java - 로또 객체
│ │ LottoResult.java - 당첨 내역
│ │ Lottos.java - 구매한 로또 객체
│ │ Rank.java - 순위에 따른 당첨 개수, 보너스 일치 여부, 상금 enum 객체
│ │ WinningLotto.java - 당첨 번호를 가지고 있는 객체
│ │
│ ├─util
│ │ Transform.java - data의 형태를 변환하고 형변환하는 util 함수
│ │
│ ├─validator
│ │ LottoValidator.java - 로또 숫자에 대한 예외 처리
│ │ NumberValidator.java - 한 개 숫자에 대한 예외 처리
│ │
│ └─view
│ InputView.java - Console에서 Player의 input을 받기
│ OutputView.java - Console에서 Player에게 output 보여주기
객체가 스스로 일을 할 수 있도록 했다. 그리고 상태를 외부에서 접근하지 않도록 구현했다.
✔️ LottoResult
- 로또 결과를 가지고 있는 객체이다. 결과를 가지고 있는 객체가 일할 수 있도록 수익률이나 상금 같은 정보가 어떻게 계산되는지 외부에서는 모르게 처리했고 결과만 전달받는다.
✔️ Lotto
- Lotto 개체에 상태를 getter로 꺼내서 외부에서 비교하는 방식이 아닌 상태를 가지고 있는 개체에 질문을 보내 결과를 얻었다.
✔️ Lottos
- 기능 구현 중 구매한 로또 번호를 로그 형태로 출력하는 경우가 있다.
- 그래서 나는 유저가 보는 데이터를 필요한 형태로 가공해서 넘기기 위해 toString 메서드를 재정의했다.
- 이후에 이펙티브 책을 읽었는데 유저 데이터는 DTO로 넘기고 toString은 객체를 확인하고 디버깅을 좀 더 편리하려고 쓰는 메서드라는 것을 알게 되었다.
equals와 hashCode 규약만큼 대단히 중요하진 않지만, toString을 잘 구현한 클래스는 사용하기에 훨씬 즐겁고, 그 클래스를 사용한 시스템은 디버깅하기 쉽다.
실전에서 toString은 그 객체가 가진 주요 정보 모두를 반환하는 게 좋다.
toString은 해당 객체에 관한 명확하고 유용한 정보를 읽기 좋은 형태로 반환해야 한다.
- 이펙티브 자바 아이템12. toString을 항상 재정의하라
✔️ Rank
- enum을 좀 더 잘 활용하게 되었다.
객체에 대한 이해가 없었으니 코드를 처음처럼 짜는 것은 어쩌면 당연하다.
오히려 저렇게 먼저 짠 덕분에 객체에 대해서 공부하면서 적용해볼 수 있었다.
리팩토링한 코드는 아래에서 확인할 수 있다!! (꽤 많이 발전했다!!! 대견해)
📌 Reference
- 객체를 객체스럽게 사용하도록 리팩토링해라. (자바지기 포비)
- getter를 사용하는 대신 객체에 메시지를 보내자
- 객체지향의 사실과 오해, 위키북스, 조영호 지음
'대외활동 > [미션] 우아한테크코스 프리코스 | Java' 카테고리의 다른 글
[우아한테크코스 5기 백엔드] 지원 및 최종테스트 후기 (6) | 2022.12.29 |
---|---|
[우아한테크코스] 숫자 야구(baseball) 리팩토링 (0) | 2022.11.28 |
[우아한테크코스] 프리코스 4주차 : 다리 건너기 (bridge) (0) | 2022.11.19 |
[우아한테크코스] 프리코스 3주차 : 로또 (lotto) (0) | 2022.11.13 |
댓글