Search
Duplicate
🚀

3단계 - 자동차 경주

1. 객체 생성이 필요없는 클래스에는 생성자 접근제어자를 private로 하자

public class Constant { ... private Constant(){} }
Java
복사

2. 불필요한 Wrapper 클래스 정의를 지양하자

public class RacingInfomation { private final Integer numberOfCar; private final Integer playCount; ... }
Java
복사
불필요한 Wrapper 클래스 정의는 불필요한 boxing/unboxing이 일어날 수 있다.

3. 인터페이스 분리의 목적을 생각하라.

: 인터페이스를 매번 분리하는것은 상속객체들의 복잡도를 증가시키기에 효율적이지 않다.
기능이 변경되거나 추가될 가능성이 있는 경우, 또는 짧은 시간내에 요구사항이 변경될 예정 인 경우에 인터페이스 분리를 하도록 하자.

4. 중괄호는 생략하지 않는것이 좋다.

5. View를 위한 기능이 도메인 객체에 있을 필요는 없다.

//worst case public void go(RacingGame game) { if (allowMove(game.getNumber())) { skidMark.append("-"); } }
Java
복사
View를 위한 기능(skidMark.append("-"))이 도메인 객체에 있을 필요는 없다. 도메인 객체는 View와 같은 UI 클래스의 변화와 관계없이 독립적으로 동작할 수 있도록 구현하는것이 중요하다.
현재는 View의 요구사항이 -출력이지만 다른 값으로 변경 될 수도 있으니 고려해야한다.
도메인 객체가 독립적으로 행동할 수 있도록 변경하자.

6. 테스트를 위해 Prudction코드를 변경하는 것은 권장되지 않는다.

: 도메인 클래스와 UI클래스를 분리하여 도메인 클래스는 각각 상태만 가지도록하자.
*전략 패턴(Strategy Pattern)을 사용하여 테스트에 다른 전략으로 로직을 수행할 수 있게 해보자.

7. 빈 블럭은 새 줄 없이 중괄호를 닫는게 좋다.

//worst case private testFunction(){ } //good case private testFunction(){}
Java
복사

8. null을 반환하는 것은 안티 패턴

null은 객체가 아니기에 해당 메소드를 호출한 곳에서 반환값을 객체로 취급하게 되면 에러가 발생하게 된다. null을 리턴할 수 있다는 것은 이 메서드를 호출한 쪽에서는 null인지 아닌지를 체크해야 되는데 null 처리가 취약한 코드에서는 NPE 발생 확률이 높다.
개선방안
1.
Optional 객체를 사용
2.
null check를 메소드화 하여 호출

9. value == 0 보다는 value < 0

: value가 1~9의 값이여야 한다고 가정할 때 등치 부등호 비교보다는 음수 조건까지 고려한 비교부등호를 사용하는게 좀 더 견고한 코드가 된다.

10. 메소드 체이닝 사용시 가독성을 위해 한 줄에 하나의 dot(.)만 사용하자.

: 약간의 주관이 섞여있으며 상황에 맞춰 사용하자.

11. 한 코드내에 많은 클래스가 있으면 가독성문제가 생기는데 어떻게 할까?

class TestClass extends originalClass{ ... } class Test{ ... }
Java
복사
리뷰어: 내부 클래스도 좋지만 한 곳에 많은 클래스가 선언되어있으면 가독성이 저해된다. 패키지 관리와 접근 제한을 통해 클래스별로 파일을 만들어도 좋을 것 같다.
참가자: 테스트 클래스의 내용이 테스팅 클래스의 내용에 강결합 되어있기 때문이다.
해당 클래스는 오직 이 클래스에서만 의미가 있고 별도 클래스로 만들 경우 패키지내에 봉합된 클래스들이 많아져 분석이 어려워진다. Production코드에서는 이래서 안되지만 해당 클래스가 테스트 내부에 적합하다고 테스트를 위해 허용(희생)할 수 있는 범위라고 본다.
리뷰어: 말씀하신대로 해당 클래스의 내용에 강결합이 되어있는것 같다. 그렇기에 내부 클래스 사용은 좋은 접근 방법인것 같다. 하나의 패키지 내에서 별도 클래스로 만들어 관리하는게 클래스의 규모를 축소하고 가독성 측면에서 좋지않을까 생각해서 냈던 의견이다.
개인적 정리: 내가 생각한 설계에 맞게 클래스 분리&결합을 하도록 하며 클래스의 규모가 커질수록 패키지내부에 분리하는걸 기준으로 하자.

12. 키워드 작성 순서 Java Convention

1.
Annotations
2.
public
3.
protected
4.
private
5.
abstract
6.
static
7.
final
8.
transient
9.
volatile
10.
synchronized
11.
native
12.
strictfp

13. 상태를 갖지 않는 클래스는 유틸성 클래스

: 상태를 갖지 않는 유틸성 클래스는 정적 메소드를 고려하자.
Example
public class InputView{ private static final InputView inputView = new InputView(); private InputView() { } public static InputView getInstance() { return inputView; } private Integer questionCarNumber(Scanner scanner) { System.out.println("자동차 대수는 몇 대 인가요?"); return scanner.nextInt(); } private Integer questionPlayCount(Scanner scanner) { System.out.println("시도할 회수는 몇 회 인가요?"); return scanner.nextInt(); } public RacingInfomation execute() { Scanner scanner = new Scanner(System.in); Integer carNumber = questionCarNumber(scanner); Integer playCount = questionPlayCount(scanner); return new RacingInfomation(carNumber, playCount); } }
Java
복사

14. 프로덕션 코드에 테스트를 위한 메소드 구현은 지양하라

: 테스트를 위해 클래스 내부에 테스트용 메소드를 만드는것은 지양하는게 좋다.
개선 방안
가짜객체를 만들어서 테스트를 한다.
전략패턴을 사용해 내부 로직의 전략을 변경할 수 있도록 한다.

15. 일급 컬렉션의 사용을 지향하자.

소트웍스 앤솔로지 라는 책의 객체지향 생활체조 파트에서 언급된 내용
간단한 예제
기존의 콜렉션
Map<String, Integer> map = new HashMap<>(); map.put("one", 1); map.put("two", 2); map.put("three", 3);
Java
복사
일급 콜렉션
public FirstClass{ private Map<String, Integer> maps; public FirstClass(Map<String, Integer> maps){ this.maps = maps; } }
Java
복사
일급 콜렉션의 이점
1.
비즈니스의 종속적인 자료구조 → 특정 비즈니스에 조건들이 포함된 콜렉션 구성이 가능해진다.
2.
불변
→ 값 변경은 가능한 콜렉션과 다르게 불변 객체 생성이 가능해진다.
3.
상태와 행위를 한 곳에서 관리
→ 값과 로직이 함께 존재하여 관계있는 로직을 한 곳에서 관리하여 코드 중복과 흩어질 확률을 줄인다.
4.
이름이 있는 콜렉션
→카테고리는 같지만 상세분류가 다른 콜렉션을 구분지을 수 있다.

16. DTO 성격의 클래스는 값의 불변성이 보장되야 한다.

: DTO(Data Transfer Object)는 프로세스간 데이터를 전달하는 객체로 내부의 값이 중간에 변경되거나 해서는 안되기에 불변성이 보장되야 한다.

17. 대부분의 private 메소드는 테스트하지 않아도 된다.

private 메소드는 public메소드에서 기능을 하나씩 분리하여 만들어지기 때문에 public 메소드를 여러 케이스로 테스트하면 간접적으로 같이 테스트가 되는 것으로 볼 수 있다.(ex: Facade pattern class)
하지만, 그렇지 않은 경우도 있는데, 그럴 경우 분리해야 하는 클래스가 합쳐져있는지 고려할 필요가 있다.

18. 과도한 확장성 고려 개발은 오버엔지니어링이다 or 아니다.

참가자: 추후 개발해야할 요구사항에도 특정 기능에 대한 요구사항이 없는데 이 부분에도 확장성을 고려해 미리 작업해두는건 오버엔지니어링이 아닐까요?
리뷰어: 리소스가 굉장히 많이 필요한 정도가 아니라면 확장가능성을 항상 고려해야 합니다.
개인적 정리: 맨먼스에 산정해야할 정도가 아니라면 확장가능성을 고려하도록 하자.

19. 인자를 그대로 내보내는 getter의 사용은 지양하자

: 멤버 변수가 외부에서 필요한 경우 어떤식으로든 가공해서 보내주도록 한다.

20. 객체간의 순환참조를 주의하자

: 객체간의 관계에서 서로에 객체정보를 알고있으면 객체간의 순환참조가 발생할 가능성이 높아진다. 그렇기에 되도록 단방향 의존관계를 맺는게 좋다.
이것은 JPA의 관계매핑과 연관지어 생각하면 편한데, JPA에서 엔티티 양방향 관계매핑을 하게될 경우 오버라이딩하지 않고 toString()을 호출하게되면 서로가 서로를 호출하며 순환참조 에러가 발생하게 된다.

21. 팩토리 메소드보다는 정적 팩토리 메소드 패턴

리뷰어: 객체 생성을 다른 도메인의 책임으로 전담하면 직관적인 설계까 가능하지만, 생성될 객체의 추가정보들이 늘어날수록 파라미터는 늘어나게되는데 이 때 팩토리 클래스에서 이런 추가적인 파라미터를 알아야 할 필요가 있을지에 대해 고민해봐야한다.
특정 도메인이 외부로 노출되는 점과 생성시 필요한 비즈니스 로직이 분산될 가능성이 존재하여 Factory Method Pattern은 선호되지 않는다.

22. 메소드로 행위를 나타내는것을 지향하자.

:메소드로 행위를 나타내는 것과 코드로 풀어쓰는것은 큰 차이가 있다.
중요한 행위(로직)는 다른 사람들이 보더라도 알아보기 쉽게 구현하는것이 중요하다.
//worst case public void go(){ ... progress += 1; ... } //good case public void go(){ ... move(); ... }
Java
복사

23. 메소드의 반환값으로 Optional 객체를 전달하는것은 안티패턴

비어있는 객체를 반환하거나 Exception을 던지도록하자
예시
public Member getMember(Long userid){ Optional<Member> optionalMember = memberRepository.findByCondition(userid); return optionalMember.orElseGet(Member:new); }
Java
복사

24. 일급 컬렉션의 네이밍은 보편적으로 복수형 명사를 사용한다.

: CarList 보다는 Cars

25. 생성자에서 멤버변수를 자체적으로 만들기보단 외부에서 주입하는 방식으로

: 외부에서 값을 주입하여 객체를 생성하도록 하면 더 유연한 코드 작성이 가능하다.

26.