Search
Duplicate

6. [클린 코드 with Java] 5단계 - 자동차 경주(리팩토링)

링크
점수
⭐️⭐️⭐️⭐️
완료일
2023/04/22
상태
완료
유형
인강

리팩토링 요구사항

핵심 비지니스 로직을 가지는 객체를 domain 패키지, UI 관련한 객체를 view 패키지에 구현한다.
MVC 패턴 기반으로 리팩토링해 view 패키지의 객체가 domain 패키지 객체에 의존할 수 있지만, domain 패키지의 객체는 view 패키지 객체에 의존하지 않도록 구현한다.

피드백

리뷰어 : 사용자 정의 예외는 enum이 아닌, RuntimeException을 상속 받는 클래스로 정의해보면 어떨까요?
생각정리 : enum 도 좋은 방법이라고 생각했는데, RuntimeException을 상속 받아서 출력하는 것 을 원했던 것 같다.
public enum CustomException { NAME_MAX_LENGTH(5, "이름이 다섯자 이상입니다."); private final int length; private final String message; CustomException(int length, String message) { this.length = length; this.message = message; } public int getLength() { return length; } public String getMessage() { return message; } }
Java
복사
다음과 같이 변경
package study.step5.exception; public class NameMaxLengthException extends RuntimeException { public NameMaxLengthException(String message) { super(message); } }
Java
복사
리뷰어 : 요구사항에 따르면 자동차는 4 이상의 숫자를 전달 받은 경우만 이동할 수 있습니다. 0부터 3까지의 숫자가 전달되면 자동차의 위치 값이 0으로 유지되어야 하지 않을까요?
생각정리 : 파라미터로 전달하면 사진처럼, 표시가 되서, 이동한지 멈춘지 한눈에 볼 수 있어서 작성하였는데, 성공하는 경우만 작성해야 되는 건가요?? 질문을 했는데. 다른 사람이 봤을 경우 왜 실패했는지 확인 할 것 같다. 통과하는 테스트 코드만을 작성하자.!
리뷰어 : 모든 테스트는 통과되도록 작성해주셔야 합니다. 0부터 3이라는 숫자가 전달됐을 때 기대한대로 자동차가 움직이지 않는 것을 검증하는 것이 맞지 않을까요? 지금처럼 실패하는 테스트 코드가 남아 있으면 다른 개발자분들이 봤을 때 해당 기능이 정상적으로 동작한다고 생각하기 어려울 것 같네요.
리뷰어 : Cars 에 정적 팩토리 메서드를 추가해보는 건 어떨까요? (ex. Cars cars = Cars.from(carNames))
생각정리 : 의미적으로 한눈에 볼 수 있어서 좋았다.
public class Track { private static final String DELIMITER = ",|:"; private int attemptCount; private Cars cars; public Track(final String carNames, final int attemptCount) { this.attemptCount = attemptCount; createCars(carNames); } private void createCars(final String carNames) { List<Car> cars = new ArrayList<>(); for (String carName : carNames.split(DELIMITER)) { cars.add(new Car(carName)); } this.cars = new Cars(cars); }
Java
복사
다음과 같이 변경
public class Track { private int attemptCount; private Cars cars; public Track(final String carNames, final int attemptCount) { this.attemptCount = attemptCount; createCars(carNames); } private void createCars(final String carNames) { this.cars = Cars.from(carNames); }
Java
복사
public class Cars { private static final String DELIMITER = ",|:"; private List<Car> cars; public Cars(List<Car> cars) { this.cars = cars; } public static Cars from(final String carNames) { List<Car> cars = new ArrayList<>(); for (String carName : carNames.split(DELIMITER)) { cars.add(new Car(carName)); } return new Cars(cars); }
Java
복사
리뷰어 :
객체지향 생활 체조 원칙의 규칙 4: 한 줄에 점을 하나만 찍는다.를 참고해주세요.
지역 변수 선언 없이 곧바로 반환해도 충분할 것 같습니다.
메서드 이름을 잘 지어주셔서 isWinner()처럼 따로 메서드를 분리할 필요가 없을 것 같아요.
생각 정리 : 설명해 주신대로 변경하면 한눈에 보기 편하다.
public List<Car> getWinners() { int winnerPosition = getWinnerPosition(); List<Car> winners = cars.stream().filter(car -> isWinner(car, winnerPosition)) .collect(Collectors.toList()); return winners;
Java
복사
다음과 같이 변경
public List<Car> getWinners() { int winnerPosition = getWinnerPosition(); return cars.stream() .filter(car -> car.isWinner(winnerPosition)) .collect(Collectors.toList()); }
Java
복사
리뷰어 : 추후에 자동차 이동 조건에 대한 요구사항이 변경되더라도 자동차가 유연하게 움직일 수 있도록 교육 자료의 자동차 경주 피드백을 참고하여 전략 패턴을 적용해보면 어떨까요?
public class Car implements Comparable<Car> { private static final int STARTING_CONDITION = 4; private Position position; private Name name; public Car(final String name) { this.name = new Name(name); this.position = new Position(); } public void move(int randomInt) { moveCar(isMove(randomInt)); }
Java
복사
다음과 같이 변경
자동차에서는 인터페이스로 정의하여 DI 의존적 주입으로 만들고
외부에서 해당하는 인터페이스를 구현하여 move의 값을 넘긴다.
이제 우리는 언제든지 해당하는 값들을 인터페이스를 상속받아서 유연하게 구현 할 수 있다.
package study.step5.domain.strategy; public interface MoveStrategy { int move(); }
Java
복사
package study.step5.domain.strategy; import java.util.Random; public class CarMoveStrategy implements MoveStrategy { private static final int RANDOM_RANGE = 10; private static final Random RANDOM = new Random(); @Override public int move() { return RANDOM.nextInt(RANDOM_RANGE); } }
Java
복사
public class Car implements Comparable<Car> { private static final int STARTING_CONDITION = 4; private Position position; private Name name; public Car(final String name) { this.name = new Name(name); this.position = new Position(); } public void move(MoveStrategy moveStrategy) { moveCar(isMove(moveStrategy.move())); }
Java
복사

후기

오늘은 소스코드를 mvc 패턴으로 변경해 보았고, 정적 팩토리 메서드 패턴, 전략 패턴을 사용해 보았다. 해당하는 패턴을 이용해서 소스코드를 좀 더 간결하고, 유연하게 변경할 수 있다는 것을 알 수 있었고 언제든지 바뀔 것 같은 기능에 대해서는 전략 패턴을 사용해 보는 것이 좋을 것 같다!.
깃허브 링크
4564
pull
출처