평소에 자바를 어느 정도 잘 안다고 생각하였지만, 실제 어떠한 요구사항이 있었을 때 이를 구현하는데 생각보다 어렵다는 것을 느꼈다.
기본기가 매우 중요하고 자바를 이론으로 아는것과 실제 적용하며 몸으로 깨닫는 것은 매우 큰 차이가 있기에,
평소에도 도움을 많이 받는 친구에게 코드리뷰를 받으며 블랙잭을 구현해 보았다.
그 과정에서 내가 느끼고 배운 것을 정리하려고 이 글을 작성한다.
배운 점
기본적인 자바 컨벤션, 클린코드, 네이밍
평소에도 이론적으로는 어느 정도 알고 있다고 생각하였지만, 스스로가 많이 놓치고 있는 부분이었다.
개행을 어느 부분에서 해야 하고, 메서드 순서를 어떤 식으로 해야 하는지에 대해 무지했던 것이다.
우선 개행의 경우 변수를 선언할 때 접근자가 달라지거나 final 키워드의 유무에 따라 개행을 통해 구분해 주었다.
또한 클래스 시작 후 개행을 적용하고, 가독성을 위해서라면 필요에 따라서도 개행을 적용하였다.
이후에는 메서드 순서에 대해 다시 의식하고 적용하였다.
- public 주석
- 클래스
- 정적 변수 : public -> protected -> private
- 인스턴스 변수 : public -> protected -> private
- 생성자
- 정적 메소드 : static 메소드 (main 메소드가 있다면 static 메소드 이전에 작성)
- 메소드 : 접근자 기준으로 작성하지 않고, 기능 및 역할별로 분류하여 기능을 구현하는 그룹별로 작성이 이루어지도록 해야 한다. (public 사이에 private 메소드가 존재할 수 있다)
- 스탠다드 메소드 : toString, equals, hashcode 와 같은 메소드
- getter, setter 메소드 : 클래스의 밑 부분에 위치
메서드의 경우 public -> private 순으로 가지만, 가독성을 위해 기능 구현별로 그룹화해 줄 수 있다. 이 부분을 크게 신경 썼다.
아직까지도 어색하지만 적용하며 익숙해지고 있다.
가장 어려운 부분은 네이밍이다. 정말 개발자들이 가장 어려워하는 부분이 아닐 수가 없다. 구현을 진행하면서도 의식적으로 네이밍에 신경을 썼지만, 내가 봐도 아직 부족하다. 하지만 어느 정도의 기준을 세워서 적용하니 조금은 익숙해지고 있다.
일단 메서드는 동사/ 전치사로 시작해야 한다. 또한 이름은 명확하게 드러나도록 작성해야 한다.
아래는 자주 사용되는 메서드명이다.
- init - 데이터를 초기화
- is/has/can - boolean값 리턴 is - 맞는지 틀린 지, has - 가지고 있는지 can - 할 수 있는지
- find - 데이터 찾기
- to - 객체를 다른 형태의 객체로 변환
- A-By-B - B를 기준으로 A를 하겠다.
정적팩토리 메서드
이번에 정적팩토리 메서드를 많이 사용하였다.
처음에는 생성자를 사용해도 되는데, 왜 굳이 정적팩토리 메서드를 사용해야 하는지에 대한 의문이 많았다.
이를 적용하면서 느낀 장점은 우선 네이밍을 줄 수 있다.
new로 생성할 경우에는 이름을 줄 수 없다.
하지만 정적팩토리 메서드를 사용하면 이름을 보고 어떠한 의도인지 파악할 수 있다.
또한 Dto를 사용할 때 정적팩토리 메서드를 자주 사용하였는데, 이때 캡슐화를 잘 사용할 수 있었다.
만약 정적팩토리 메서드를 사용하지 않았다면, DTO의 생성자의 내부 구현을 모두 알아야 할 것이다.
이를 통해 좀 더 정적팩토리를 사용하는 이유나 장점을 익히게 되었다.
이후에 알게 된 사실인데, 정적 팩토리 메서드에도 네이밍 컨벤션이 존재한다.
- from - 하나의 매개 변수를 받아서 객체를 생성
- of - 여러 개의 매개 변수를 받아서 객체를 생성
- getInstance / instance - 인스턴스를 생성, 이전에 반환했던 것과 같을 수 있음
- newInstance / create - 새로운 인스턴스를 생성
- get [OtherType] - 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음
- new [OtherType] - 다른 타입의 새로운 인스턴스를 생성
상속과 조합
구현하면서 가장 많이 고민하였던 부분이다. 상속과 조합
우선 내가 고민했던 부분과 이에 대한 질문을 했었다.
이를 간단하게 요약하면 dealer와 player가 존재하는데 공통 로직이 존재하며(인스턴스 변수를 활용), 인스턴스변수도 공통되었다.
하지만 인스턴스 변수를 활용하는 메서드에서 차이가 발생하는 상황이 있었다. 이를 두고 고민을 하였다.
첫 번째는 인터페이스를 사용에 대해 고민하였다.
하지만 이를 사용하지 않았다.
우선 장점으로는 dealer와 player를 공통으로 묶기 때문에 이후 다양성을 활용하여 유연하게 사용가능하다.
또한 구현체에서는 단순하게 인터페이스를 구현한 것이기에 큰 부담감이 없다.
하지만 중복로직이 발생한다는 단점이 있었다.
두 번째는 조합을 사용하는 것에 대한 고민이었다.
조합을 사용하면 원하는 부분을 사용할 수 있다는 장점이 있었다.
또한 상속보다 유연하다는 장점이 있었다.
하지만 스스로가 느끼기에 단순하게 인스턴스 변수에 있는 메서드를 다시 가져와서 사용한다는 느낌을 강하게 받았고,
이 또한 코드 중복이 발생한다는 단점이 있었다.
마지막으로는 상속을 사용하는 것이었다.
사실 상속은 매우 강력한 의존이기에 is-a관계가 확실할 때만 사용하는 것이 맞다고 한다.
물론 상속을 사용할 경우 결국 자식클래스에서 부모의 클래스를 알아야 하기에 캡슐화도 깨진다는 단점이 존재하였다.
하지만 중복되는 코드를 줄일 수 있었고, 다형성을 활용하여 유연하게 사용할 수 있다는 장점이 있기에 상속을 사용하여 해결하였다.
정리
사실 이외에도 하면서 느끼거나 배운 것이 정말 많다.
우선 설계가 정말 중요하다는 것을 느꼈다.
처음에 설계를 깊게 하지 않고 시작했다가 바로 꼬여버리는 상황이 발생했다.
꼼꼼한 설계는 무조건 필수이다.
맨 처음 언급했듯이 어떤 것이 왜 좋고, 왜 안 좋은지에 대한 이론만 알고 있었다.
직접 구현하며 몸의 느끼니 어떤 상황에서 어떠한 것을 사용할 때의 트레이드오프를 느끼게 되어 재미있었고 더 성장했다고 느꼈다.
역시 이를 통해 가장 크게 느낀 것은 정답은 없다는 것이다.
어떠한 상황에서 트레이드오프는 반드시 발생한다.
각각의 장단점을 알고 유연하게 적용할 수 있는 실력을 길러야겠다.
위의 내용 말고도 받은 피드백에 관한 링크는 아래에 걸어두었습니다.
https://github.com/kimtaesoo99/java-blackjack/pull/1
'JAVA' 카테고리의 다른 글
[JAVA] 숫자 야구 게임을 구현하면서 배운점 (4) | 2023.10.27 |
---|---|
[JAVA] 체스를 구현하면서 배운점 (0) | 2023.07.06 |
[JAVA] 리플렉션(Reflection) (0) | 2023.05.23 |
[JAVA] 불변 객체를 사용해야 하는 이유 (2) | 2023.05.17 |
다양한 가비지 컬렉션(Garbage Collection) 알고리즘 (0) | 2023.05.15 |