상속과 조합

조합은 하나의 객체가 자신이 필요한 객체를 가져다가 필드에 선언해서 사용하는 형태를 의미한다.

결합도란 한쪽에서 수정을 가했을 때 다른 쪽에서 수정이 많이 일어나고 있다면 결합도가 높은것이다.

- Cell에 있는 속성을 CellState로 옮기자.

- private으로 바꾸자.
- 필드와 관련된 메서드들을 Cell로부터 가져오자.


- EmptyCell이 생성되는 순간 CellState도 생성이 된다.

- 부모에 있던 필드들을 이용했던 getSing() 메서드를 CellState에 요청하는 방법으로 바꿔보자.


- CellState에 isFlagged() 생성

- Cell을 복사하여 Cell2 인터페이스 생성하자.









- 모두 Cell2 인터페이스로 바꾼 후 Cell 클래스를 지우자.
- 부모에 있던 isFlag, isOpened와 관련된 메서드들을 조합의 형태로 풀어냈기 때문에 좀 더 유연해졌다.
-> 기존에는 부모가 변경되면 자식들에게 영향을 미쳤지만 CellState로 분리함으로써 결합도가 낮아졌다.
Value Object


value라는 변수가 final로 선언되어 있다.
-> 불변성 보장
생성자에서 유효성 검사를 하고 있다.
== 은 동일성 검사이고 equals는 동등성 검사이다.
우리 도메인에서 Money는 단위 객체이다.
-> int, long 같은 기본 타입들처러 Money라는 것을 우리 도메인에서 기본 타입으로 쓸거라고 받아드려도 된다.
-> 불변성, 동등성, 유효성 검사를 했다면!



엔티티는 식별자만 같으면 동등하다고 보며 그 외에 값이 다르다면 시간이 지남에 따라 변화했구나 라고 이해하면 된다.
- rowIndex와 colIndex는 함께 있을때 의미가 있다.
-> 이를 묶어서 value Object로 리팩토링 해보자.

- valueObj는 동등성, 유효성, 불변성을 보장 받아야 한다.
-> 불변성 : final 키워드를 통해 해결
-> 유효성 : index 기반이기 때문에 0 초과
-> 동등성 : equals와 hashcode 오버라이드 해서 해결

- getCellInputFromUser에서 inputHandler가 String을 반환하는게 아니라 CellPosition을 만들어서 반환하는건 어떨까?

- 이전에 변환하는 과정을 모두 boardIndexConverter로 이관했었다.
-> inputHandler로 옮기자.

- InputHandler 인터페이스에 스팩 추가하자.








- Board라는 것은 Minesweeper의 것이다.
-> 사용자 입력을 받는 곳에서 Board의 정보를 알 필요가 있을까?
-> 유효성 검증 로직을 분리하자.






- CellPostion을 받아서 Gameboard를 가지고 있는 Minesweeper에서 검증을 하자.
-> 인덱스의 기능(0이상)은 이미 valueObj, inputHandler에서 검증을 했다.
-> row, col만 넘는지 유효성 검사를 하면 된다.

- getter를 써서 비교했을 때 자연스러운면 사용해도 되겠지만 일반적으로 무례한 행동이다.
-> CellPosition에게 물어보자.

- 메서드 입장에서는 rowSize인지 모른다.
-> 파라미터를 rowIndex로 바꿔주자.
-> Col도 마찬가지
- CellPosition 기반으로 리팩토링 해보자.









- CellPosition 하나밖에 없으니까 전차사 At을 사용해보자.












- row < 0, col < 0은 이미 유효성 검사를 하기 때문에 제거해도 된다.

- CellPosition을 넣어주려고 하는데 유효성 검사가 마음에 걸린다.
-> CellPosition의 row가 0이었다면 계산한 -1 값이 메서드로 들어가기 때문에 에러가 발생할 것이다.
-> 이를 해결하기 위해 상대좌표를 의미하는 RelativePosition이라는 VO를 만들어주자.






- 메서드 안에 있으면 계속 생성하니까 상수로 만들자.
-> 외부에서 참조 가능해야 하니 public으로 변경










- 두 메서드간 연관관계가 없다.
-> 외부에서 calcualatePostionBy를 그냥 사용하면 에러가 날 것이다.




- 위 검증로직을 스트림에서 해도 상관 없을 것이다.
- 검증로직에서는 board size가 넘는지를 검사했지만 스트림에서는 board size를 넘지 않는 것들에 대한 것만 filter처리를 할 것이다.
















일급 컬렉션






- GameBoard에서 스트림을 돌리는 로직이 너무 부담스럽다.
-> 일급 컬렉션을 이용하자.


- board로 부터 만들니 from으로 만들어보자.
-> 스트림을 일급 컬렉션 내부로 옮길 것이다.




- initializeGame()에 지뢰가 중복 생성되는 버그가 있었다.
-> 이를 CellPosition에 대한 일급 컬렉션을 만들어 해결해보자.


- 이중 for문을 돈다는 이야기는 전체 셀 포지션 위치를 다 돌고 싶다는 의미이다.
-> 어떤 보드가 있을 때 그 보드에 대한 모든 셀 포지션을 먼저 만들고 그 다음에 뭔가 작업을 해주면 어떨까?




- 외부에서 필드에 있는 컬렉션을 참조 할 수 없도록 새로운 컬렉션을 만들어서 반환해야한다.
-> 일급 컬렉션 주의점!


- landMineCount만큼 CellPosition을 뽑으면 된다.











- 지뢰가 아닌 셀들을 대상으로 카운트를 센 다음에 카운트가 0이 아니면 업데이트를 하고 있다.

- cellPosition에게 어떤 positions를 줄테니 그거 빼서 리스트 달라고 요청하자.







- 같은 인스턴스를 넣어줬기 때문에 생긴 오류이다.
-> 성급한 메서드 추출의 잘못된 리팩토링

Enum의 특성과 활용


Enum은 코드이기 때문에 변경하려면 배포를 해야한다.
따라서 Enum의 변경이 잦다면 DB에 저장하는 것이 더 좋을 수 있다.

- inputHandler가 User로부터 1, 2를 받아서 String을 반환하는게 아니라 Enum을 반환하는게 어떨까?












- Cell을 어떻게 그릴지는 InputHandler에서 결정하는게 자연스럽다.
-> getSign()에서 Cell이 판단하여 어떻게 그릴지 알려주는 게 이상하다.
- 따라서 inputHandler에게 Cell에 대한 정보를 넘겨줘야 한다.
-> SnapshotStatus을 만들어서 넘겨주자.
-> 어떤 상태인지 알려주는 Enum이다.

- snapshotStatus을 리턴하려고 했더니 특이하게 NumberCell은 자신의 숫자도 표시해야 한다.
-> nearbyLandMineCount와 snapshotStatus를 포함하는 CellSnapshot이라는 클래스를 하나 만들자.


- Cell이 CellSnapshotStatus의 정보를 알아야 할까?
-> 정적 팩토리 메서드로 만들어주자.






- outputHandler에 가서 스냅샷 정보를 받아서 그려주자.
-> CellPosition에 알맞은 스냅샷좀 줄래?







- getSign() 사용 안하니 삭제하자.
- Sign 상수들의 사용이 모두 outputHandler로 갔으니까 옮겨주자.


- 정리 : 셀을 보니까 셀에 어떻게 그릴지에 대한 책임이 셀에 같이 묶여있었으며 SRP를 위반하고 있는 것 같아서 그리는 책임은 무조건 OutputHandler로 가져가자.
-> 그래서 분리하려고 봤더니 셀이 지금 어떤 상태인지에 대한 정보가 필요해서 스냅샷이라는 개념을 도입했다.
-> 스냅샷 상태에 따라 분기로 나눠 어떤 것을 그릴 지 반환하도록 하였다.


- SnapshotStatus와 nearbyLandMineCount가 모두 같으면 같은 스냅샷으로 봐야할 것이다.
-> ValueObj 이다.
-> equals, hash 오버라이드 하자.
다형성 활용하기



- CellSignProvidable 인터페이스를 만들자.
-> 스냅샷을 넘겨줬을 때 그에 맞는 사인을 리턴해주는 역할을 한다.








- 필드로 가지고 있던 사인들 제거
- 아직 if 문을 제거하지 못했다.
-> CellSignProvidable에 스팩을 추가해보자.








- 반복문 안에 있어서 List를 계속 만들것이다.
-> 리스트를 포함하고 있는 클래스를 하나 만들자.


- CellSignFinder도 계속 생성할 필요 없이 한번만 생성하면 된다.
-> 필드로 가지고 있자.


- CellSign 클래스가 더 생성되면 클래스를 추가해서 CellSignProvidable을 구현해야하고 CellSignFrinder에 구현체를 넣어줘야 한다.
-> 사람이 실수 할 만하다.
-> Enum으로 이 문제 해결










클래스를 추가할 때 리스트에 넣어줘야 하며 인터페이스도 구현해야하는 상황이다.
-> 하나라도 놓치면 에러가 발생하는 상황!

Enum을 사용하면 추가와 구현을 같이 해버릴 수 있기 때문에 이 방법으로 리팩토링을 하였다.

숨겨져 있는 도메인 개념 도출하기


- 마인 스위퍼가 점점 발전한다면 게임 레벨, 인풋 핸들러, 아웃풋 핸들러 말고도 뭐가 더 추가 될 수 있다.
-> 모든 설정 정보들을 묶어서 한 번에 넣어주는 객체를 하나 만들자.



