1. 클래스를 어떻게 나눠야 할까?
자바에 익숙하지 않아 처음부터 객체지향적으로 코드를 작성하기는 부담스럽고 그래서 아마도 많은 사람들이 Application에 전부 구현하고 코드를 나누는 리팩토링 과정을 거치지 않을까??🤔 나도 처음부터 객체지향적으로 구성할 능력이 아직은 부족해 전부 구현하고 리팩토링 과정을 거쳤다. 하지만 나머지 3주차와 최종 코테를를 수월하게 하기 위해서는 이 고민이 필요하다고 생각한다.
왜 고민을 해야하냐면... 현재 숫자 야구 게임은 구현의 난이도가 어려운 편은 아니다(백준 실버). 그래서 한 클래스에 몰빵해놓고 테스트 통과를 하는게 가능하겠지만 2 ~ 4주차에 점점 구현의 난이도가 높아지면 코드의 수가 많아져 이를 관리하는 것도 더 힘들어질 것이며, 따라서 한 클래스에서 몰빵으로 구현을 하기 쉽지 않은 상태가 출제 되지 않을까?
아무튼 이런저련 이유에서 클래스를 나누는 나만의 기준이나 방법을 생각하지 않으면 리팩토링하는 과정이 너무 어려워질테니 1주차에 든 생각을 서술해보겠다!
1) 메서드명에 의도를 명확하게 담자!
→ 메서드와 객체의 관계는 " 메서드의 집합 ⊂ 객체 "가 있다고 생각한다. 나는 메서드를 객체의 행동이라고 생각하는데 행동들의 집합이 물론 나를 나타내지는 못한다. 예를 들어, 걸음, 양치, 수면 등의 행동이 나를 특정할 수는 없기 때문이다. 하지만 메서드를 통해서 어떻게 클래스가 구성되어야하는지 감을 잡을 수 있다. 그러므로 메서드명에 나의 의도를 명확하게 담으면, 즉 최대한 서술적으로 메서드 명을 설정하면 리팩토링할 때 어떤 클래스에 어떤 메서드를 줘야할지 감을 잡기 쉽다.
Ex1) 처음 구현할 때 Application에 몰빵했따면, 「1~9로 구성된 서로 다른 3자리 자연수를 만드는 함수」 를 분명히 메인에 만들었을 것이다. 나는 createNumber() 함수를 만들었는데 이게 누구의 행동일까? 생각해보면..컴퓨터(상대방)의 행동이다. 따라서 Computer 클래스에서 이를 담당하면 된다.
Ex2) User의 입력에 대해서 에외처리를 해야한다. 예외처리하는 클래스가 있으면 여기서 전담하면 되겠구나라는 생각을 할 수 있다.
Ex3) countBallAndStrike는 Ball과 Strike의 숫자를 센다는 의도가 명확히 보인다.
처음부터 어떤 클래스를 만들어야겠다는건 전체적 그림이 머리속에 있는 사람만 가능하다. 따라서 나는 메서드를 통해서 어떤 클래스가 필요할지에 대해서 역으로 감을 잡았다.
2) 그림을 그려서 객체 간의 상호관계, 흐름도 그려보기!
→ 그림이라는 보조도구를 통해서 각 클래스들간의 관계를 파악 및 흐름도를 작성해보면 내가 적절한 역할을 부여했는지 확인하기 쉽다.
EX) 나는 처음에 Game클래스가 숫자를 맞춘 후에 게임의 재시작을 결정하도록 했다. Game 자체가 재시작을 할 수 있는지 생각해보면 뭔가 어색하다. 유저가 입력에 따라서 Game이 결정되어야하는데 Game 자체가 입력을 받고 결정하는 것처럼 보인다.
그래서 생각해보면 ≪누군가(Application)가 User의 입력("1")을 받아서 Game의 중지 여부를 결정하는 존재≫가 있다고 생각할 수 있다.
3) 한 메서드가 비대해지는 경우, 그 메서드가 어떤 역할을 하고 있는지 하나씩 분리시켜보기!
→ 클래스 내 메서드가 비대해지는 경우는 드 메서드가 내가 의도한 것보다 + @의 역할을 하고 있기 때문이다. 이것을 알기위해서는 내가 짠 코드의 함수들이 어떤 역할을 하는지 자세하게 분석해보면 좋다.
Ex) Game 클래스의 분석
- 사용자로부터 입력 받기 → 사용자가 입력하고 그 값을 받아오면 된다.
- computNumber 생성하기 → 힌트 계산은 컴퓨터가 하니까 userNumber 받아서 힌트 결과만 Game에 제공해주면 된다.
- 게임 종료 후 재시작 판단하기 → User가 입력을 해서 게임의 중지 여부를 결정하는데 중지 여부를 결정할 객체가 필요하다.
- 예외처리 판단하기 → 3개의 예외처리도 1개로 묶고 담당하고 있는 클래스에서 처리를 하는게 맞다.
- ......
4) 1차 리팩토링이 완료되었으면 각 클래스가 어떤 역할을 담당하는지 적어보기
→ 만들어 놓은 메서드를 메서드명으로 찢다보면 내가 생각하지 못한 메서드가 있을 수 있는데 그건 리팩토링 과정에서 반영되기 힘들다. 1차 리팩토링까지 했으니 이제 각 클래스가 어떤 역할을 하는지 명확하게 기술해보면 내가 메서드를 적절하게 부여했는지 판단할 수 있다.
Ex)
- User 클래스: 숫자를 입력 하는 객체
- Compute 클래스: computeNumber를 게임 시작할 때 생성하고 사용자로부터 입력 받은 숫자에 대한 Ball, Strike 계산
- Game 클래스: 시스템으로, User와 Compute 클래스를 중재하고 결과를 출력해 콘솔창에 보여주는 객체
- Application 클래스: 게임의 시작을 알리고, 게임을 종료할지, 게속할지 판단하는 객체
2. 사고의 흐름
- Test 통과 전: 테스트 통과를 목표로 Application에 모든 코드 구현
- 각 역할에 대해서 어렴풋이 생각은 하고 있었고 그것을 메서드명에 반영했다. 메서드명에 반영을 해서 구현 했을 때에 어떤 생각을 했는지, 이 메서드가 역할을 했으면 좋겠는지 추후 떠올리기 매우 쉬웠다.
- countBallAndStrike, countStrike
- isValidLength, isValidNumber, isAllDigit...어떤 메서드인지 직관적으로 파악이 쉬워 리팩토링할 때 편하다.
- 각 역할에 대해서 어렴풋이 생각은 하고 있었고 그것을 메서드명에 반영했다. 메서드명에 반영을 해서 구현 했을 때에 어떤 생각을 했는지, 이 메서드가 역할을 했으면 좋겠는지 추후 떠올리기 매우 쉬웠다.
- Test 통과 후 본격적 리팩토링 시작
- 1차 리팩토링(https://happy-ryan.tistory.com/35)
- '링크의 2. 진행상황'을 보면 알 수 있듯이 각 메서드의 역할에 대한 생각만 들어가 있을 뿐 그 메서드를 담고 있는 클래스가 근본적으로 무슨 역할인지 대해서는 고민을 하지 않은 것을 알 수 있다. 메서드를 통해서 간접적으로 이 클래스가 무슨 역할을 하는지 생각할 수 있을 뿐이다.
- 문제는 이렇게 메서드 기준으로 클래스가 분리되면 각 클래스의 역할이 정확하게 정해지지 않아 Game 클래스처럼 하나의 클래스가 아주 많은 일을 부담하게 된다.
- 사용자로부터 입력 받기
- computNumber 생성하기
- 게임 종료 후 재시작 판단하기
- 예외처리 판단하기
- ......
- 2차 리팩토링(https://happy-ryan.tistory.com/43)
- 실제 숫자야구 게임을 생각해보면서 각 클래스의 역할에 대한 고민을 했다.
- User 클래스: 입력 하는 객체
- Compute 클래스: computeNumber를 게임 시작할 때 생성하고 사용자로부터 입력 받은 숫자에 대한 Ball, Strike 계산
- Game 클래스: 시스템으로, User와 Compute 클래스를 중재하고 결과를 출력해 콘솔창에 보여주는 객체
- Application 클래스: 게임의 시작을 알리고, 게임을 종료할지, 게속할지 판단하는 객체
- 이런 식으로 객체의 역할에 대해서 고민을 하니까 Game 클래스를 분해하는 것이 가능해졌다.
- 사용자로부터 입력 받기 → User클래스로
- computNumber 생성하기 → Computer클래스로
- 게임 종료 후 재시작 판단하기 → Application 클래스로
- 예외처리 판단하기 → ExceptionHandler 클래스로
- ......
- 실제 숫자야구 게임을 생각해보면서 각 클래스의 역할에 대한 고민을 했다.
- 1차 리팩토링(https://happy-ryan.tistory.com/35)
'우아한테크코스 > 1주차 프리코스' 카테고리의 다른 글
[1주차 프리코스] 1주차 회고 (0) | 2023.10.26 |
---|---|
[1주차 프리코스] 인터페이스 상수 → Enum으로! (0) | 2023.10.24 |
[1주차 프리코스] 3 ~ 4일차 리뷰 (0) | 2023.10.21 |
[Java] 1주차 스터디 - 파이썬 list 자바 Array, ArrayList 에 대해! (2) | 2023.10.21 |
[Java] 컬렉션 - 맵(Map) (0) | 2023.10.21 |