자바의 일급 컬렉션

포모·2020년 12월 17일
0

Clean Code

목록 보기
4/6
post-custom-banner

TDD의 포인트, 일급 컬렉션

클린 코드, TDD에 대해 관심을 갖게 되다보면서 자연스럽게 일급 컬렉션에 대해 접하게 되는 데요,
도대체 일급 컬렉션이란 무엇일까요?


class Car {
	private String name;
	private int position;
}

class Cars {
	List<Car> cars = new ArrayList<>();
    
	...
}

역시 개념을 이해하는 데는 예제가 제일 쉬운 거 같습니다.

위의 Car라는 클래스를 여러개 만들어 사용하고 싶은데, 이때 Cars 클래스를 통해 Car Collection을 Wrapping하면서 그 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고 합니다.


비즈니스에 종속적 자료 구조

GameController.java

private void inputTimes() {
    OutputView.askTimes();
    try {
        String input = InputView.input();
        InputValidator.validTimeInput(input);
        time = input;
    } catch (NullPointerException e) {
        inputTimes();
    }
}

제가 처음 자동차 경주 게임을 진행하면서 짰던 횟수 입력 기능 메소드입니다.


횟수의 검증 로직
1. 숫자가 입력되었는가? (특수문자/한글/영어 입력 X)
2. 자연수가 입력되는가? (0 이하는 입력 X)


InputValidator.validTimeInput(input)는 서비스를 제공하는 코드에서 검증 로직이 있게 됩니다.

만약, 횟수를 GameController가 아닌 다른 곳에서도 입력 받는 경우 검증 로직이 또 사용 되어야 합니다.


public class Time {
    private int time;

    public Time(String time) {
        Time.validTimeInput(time);
        this.time = Integer.parseInt(time);
    }

    public static void validTimeInput(String time) {
        if (TimeValidator.isInteger(time)) {
            throw new NotNumberException();
        }

        if (TimeValidator.isBelowZero(time)) {
            throw new InvalidRangeTimeException();
        }
    }
    
    ...
]

횟수에 대한 일급 콜렉션을 만들어주어 그 안에서 검증 로직을 수행할 수 있도록 해줍니다.
이렇게 되면 GameController는 무엇을 수행할까요?

private void inputTimes() {
    OutputView.askTimes();
    try {
        time = new Time(InputView.input());
    } catch (NullPointerException e) {
        inputTimes();
    }
}

횟수가 생성되었다는 것을 생성자를 통해 확인할 수 있고, 서비스 메소드에서 검증 로직을 수행하지 않고 일급 콜렉션을 통해서 검증 후 값을 가집니다. 👍


불변을 보장

Java의 final은 정확히는 불변을 만들어 주는 것이 아닌 재할당을 금지합니다. (자바의 static과 final)

final List<String> list = new ArrayList<>();

list.add("123");
list.add("456");
list.add("789");

list = new LinkedList<>();	// error

listfinal로 선언 되었지만 데이터 추가/삭제가 위와 같이 자유롭습니다.

다만, new를 통해 재할당 하는 경우에는 컴파일 에러가 발생합니다. 😢 (final은 재할당을 금지한다!)


소프트웨어 규모가 커짐에 따라 불변 객체는 절대 값이 바뀌지 않는 다는 것이 보장되기 때문에 코드 변경이나 수정에 대한 부작용이 최소화된다고 합니다.

그러나 자바에서는 final로 이 문제를 해결할 수 없기 때문에 일급 컬렉션과 래퍼 클래스 등의 방법으로 해결해야 한다고 합니다.

이말인즉슨, 생성자를 통해 데이터가 생성된 일급 컬렉션Setter를 두지 않아 값을 변경/추가가 안되는 불변 컬렉션으로 만들 수 있습니다.


상태와 행위를 한 곳에 관리

일급 컬렉션은 값과 로직이 함께 존재합니다.
위에서 예를 들었던 자동차 경주의 최대 우승자를 구하는 기능을 예시로 보겠습니다.

CarsCar의 리스트를 담고 있는 일급 컬렉션입니다.
단순히 Car의 상태(position, name)만 담고 있는 것이 아닌 Car의 행위로 가지게 됩니다.

public class Cars {
    final List<Car> carList = new ArrayList<>();
    
    
    ...
    
    
    public List<String> getWinner() {
        return carList.stream()
                .filter(car -> car.isMaxPosition(findMaxPosition()))
                .map(car -> car.getName())
                .collect(Collectors.toList());
    }

    private int findMaxPosition() {
        int maxPosition = -1;
        return carList.stream()
                .filter(car -> car.aboveMaxPosition(maxPosition))
                .mapToInt(car -> car.getPosition())
                .max()
                .getAsInt();
    }
}

우승자를 찾는 getWinner 메소드와 이 우승자를 찾기 위한 최대 거리를 구하는 findMaxPosition 메소드를 가집니다. (행위)

만약, 우승자를 찾는 메소드가 Cars에 있지 않고 다른 곳에 있다면?
만약 다른 곳에 Cars를 생성하면, 그땐 또 다시 우승자를 찾는 메소드를 만들어주어야합니다. 🤦‍♀️

일급 컬렉션 애용합시다!


이름이 있는 컬렉션

실제로 사용하진 않았지만, 예시 코드를 들어보겠습니다.

List<Car> BenzList = new BenzCar();
List<Car> BMWList = new BMWCar();

같은 차 종류임에도 벤츠 일급 컬렉션과 BMW 일급 컬렉션이 각각 다르게 생성될 수 있습니다.
의미에 맞게 사용하면 검색이 쉽고 표현을 명백히 할 수 있습니다.


🛴 마무리

제가 이해한 나름대로 일급 컬렉션을 정리해보고, 그에 맞게 예시를 만들어보았는데요.
최대한 쉽게 풀어보고자 노력했습니댜 ㅠㅠ

쉽게 전달이 되었으면 좋겠네요!!
혹시 잘못된 내용이나 이해가 안 가는 부분이 있다면 댓글 환영입니다


참고

post-custom-banner

0개의 댓글