[TDD, Clean Code with JAVA] 자동차 경주 미션 2

Dayeon myeong·2021년 4월 14일
0

문자열 계산기

자동차 경주 미션의 2번째 과제인 문자열 계산기에 대해 배웠던 것이나 생각했던 부분을 적어보려한다.

기능 분리하기

  • 코드 작성 전 테스트 할 수 있는 단위로 나누어 구현 목록을 만들자. 그리고 이에 따라 클래스 설계하기..!

문자열 계산기라면

  • 덧셈
  • 뺄셈
  • 곱셈
  • 나눗셈
  • 입력 값이 null이거나 빈 공백 문자일 경우 IllegalArgumentException throw
    ...

enum 활용하기

덧셈, 뺄셈, 곱셈, 나눗셈 각각의 코드를 레거시로 만들면 아래와 같다.

public int calculate(int x, int y, String operation) {
	if (operation.equals("+") {
		return x + y;
    } else if (operation.equals("-")) {
    	return x - y;
    }
    ...
}

이렇게 코드를 짜는 것의 문제는
1. 연산이 추가될 경우, if문을 포함한 코드가 계속 늘어난다.
2. 동일한 타입의 값임에도 너무 많은 반복적인 코드가 발생한다.
3. (x, y, operation)는 따로 조회가 되고, 계산은 별도의 메소드로 진행된다.즉, 해당 인자들과 메서드가 서로 관계가 있음을 코드로 표현할 수 없다. 이렇게 될 경우 이후 기능 추가 시에 해당 메서드를 모른다면 메서드를 중복 생성할 수 있다.

해결 방법은 enum을 사용하자!

enum : 열거형이라고 불리며, 서로 연관된 상수들의 집합


public enum Operator {

    PLUS("+", (x,y) -> (x + y)),
    MINUS("-", (x, y) -> (x - y)),
    MULTIPLY("*", (x, y) -> (x * y)),
    DIVIDE("/", (x, y) -> (x / y));

    private String operation;
    private BiFunction<Integer,Integer,Integer> value;

    Operator(String operation, BiFunction<Integer,Integer,Integer> value) {
        this.operation = operation;
        this.value = value;
    }

    public int calculate(int x,int y) {
        return value.apply(x,y);
    }
    ...
}
  • "+"라는 데이터는, 더하기라는 행위와 한 곳에서 관리 할 수 있게 된다.한 눈에 서로 관계가 있음을 파악할 수 있다.
  • 각 enum 상수는 자신이 수행해야할 기능과 책임만 가지게 된다.
  • Operator 클래스에게 직접 계산을 요청할 수 있다.

    public static Operator findOperation(String operation) {
       return Arrays.stream(Operator.values())
               .filter(op -> op.getOperation().equals(operation))
               .findAny()
               .get();
    }
  • 그룹 관리가 가능해지면서 enum 상수들을 순회 하며 본인들이 갖고있는 데이터를 갖고있는지 등을 확인 할 수 있다.

정적 메서드

문자열 계산기에는 여러 예외처리 (throw 메서드)들이 있다. 이런 메서드들을 한 번에 모아 유틸리티성의 클래스를 만들었다.


public final class Validator {

    public static void isBlankOrEmpty(String input) {
        if(isBlank(input) || isEmpty(input)) {
            throw new IllegalArgumentException("입력값이 null이거나 빈 공백 문자열입니다.");
        }
    }
    ...
}

이후에 이런 유틸리티 함수를 만들 때 유용하게 사용되는 것이 정적메소드라는 것을 알게 되었다.

정적 메서드는 2가지로 간략히 볼 수 있는데.
1. 클래스의 인스턴스 없이 호출이 가능하며, 인스턴스에서는 호출할 수 없다.
2. 유틸리티 함수를 만드는 데 유용하게 사용된다.

ex) Math.min, Math.max ,,,

이런 정적메서드를 만들 때의 기준은 무엇일까?

  • 인스턴스 생성 없이도 호출할 것인가?
  • 인스턴스 변수를 사용하지 않는가?
  • 메서드가 변화되지 않는가?
  • 메서드가 여러 곳에 공유되고 있는가?

위와 같은 기준에 정적 메서드를 만들어야 한다.

하지만 이런 정적 메서드의 가장 큰 단점은 단순히 메서드만 가지고 있기 때문에 객체 지향에 벗어난 개념이 된다고한다.

예를 들어, 이번 과제에서도 "사칙연산 기호가 아닌 경우의 예외처리"는 사실 Operator 클래스의 역할과 책임이기 때문에 정적 메서드로 만드는 것보단 Operator클래스에 두는 게 맞다고 생각한다. 이후에는 이런 클래스간 역할과 책임을 더 생각해서 분리해야겠다.

참고자료
https://woowabros.github.io/tools/2017/07/10/java-enum-uses.html

https://mygumi.tistory.com/253

profile
부족함을 당당히 마주하는 용기

0개의 댓글