일단 나는 넥스트스텝의 TDD, Clean Code with Java 12기를 하면서
여기서도 일급 컬렉션을 사용했었다.
참 웃겼던건 이것을 조금 응용을 했었어야 했는데 개념 자체도 자세하게 정리가 덜 된것 같았다.
도메인에서부터 차근차근 만들어가는 것에서는 어느정도 생각이 잘 들었지만,
기존 레거시 코드에 이런게 적용되어 있지 않고 뚱뚱하게 로직이 작성되어 있으면
그냥 넘어갔던게 흔했다.
소트웍스 앤솔러지에서 발췌된 객체지향 생활체조 원칙이다.
진하게 설정해 놓은 것들은 웬만하면 거의 적용하고 있는 규칙들이다.
단, 9번 규칙에서 게터는 어쩔수 없이 사용하게 되는것 같다.
여기서 이 8번 규칙을 적용하는게 위에서 말했던 이유이다.
이 규칙 적용하는 법은 간단하다.
컬렉션을 포함한 클래스는 반드시 다른 멤버 변수는 존재하지 않아야 한다.
그리고 그 컬렉션만 들어있기 때문에 해당하는 컬렉션만의 로직을 구현할 수 있는 분기점이 생성된 것이다.
예를 한가지 들어보겠다.
public class Car {
private final String name;
private final int yearModel;
public Car(final String name, final int yearModel) {
this.name = name;
this.yearModel = yearModel;
}
public String getName() {
return name;
}
}
이런 자동차 객체를 정의 했을 때, 비즈니스 로직에서 이런 행위들을 했다고 쳐보자.
public class CarService {
public List<Car> findCarName(final String carName) {
//다른 비즈니스 로직 수행....
//DB에서 이러한 데이터를 가져왔다고 가정
List<Car> carList = Arrays.asList(
new Car("아반떼", 2021),
new Car("k3", 2018),
new Car("그랜저", 2019),
new Car("모하비", 2020),
new Car("아반떼", 2017),
new Car("아반떼", 2016),
new Car("아반떼", 2015);
);
//다른 비즈니스 로직 수행....
return carList.stream()
.filter(car -> car.getName().equals("아반떼"))
.map(car -> car.getName())
.collect(Collectors.toList());
}
}
이런식으로 하나의 로직이 어떤 곳에서 데이터를 불러오고
차들의 이름이 매개변수로 받는 carName
인 애들만 추려서 리스트로 만드는
그런 로직이다.
간단해서 나눌 필요가 없다고 생각이 들 수도 있지만, 메소드를 하나의 일만 하게끔 분리했을 때 이런 모양이 나왔다면,
그리고 이 로직들이 안에 갇혀있으면 점점 뚱뚱해지면서 테스트하기가 힘들어지는 로직이 생기게 될 것이다.
그래서 아래와 같이 개선한다.
public class Cars {
private final List<Car> cars;
public Cars(List<Car> cars) {
this.cars = cars;
}
public List<Car> findAllByCarName(final String carName) {
return cars.stream()
.filter(car -> car.getName().equals("아반떼"))
.map(car -> car.getName())
.collect(Collectors.toList())
}
}
Car
객체의 리스트만을 멤버 변수로 갖고있는 일급 컬렉션인 Cars
를 구현했고,
여기서 해당하는 비즈니스 로직을 정의해 주었다.
public class CarService {
public List<Car> findCarName(final String carName) {
//DB에서 이러한 데이터를 가져왔다고 가정
Cars cars = new Cars(Arrays.asList(
new Car("아반떼", 2021),
new Car("k3", 2018),
new Car("그랜저", 2019),
new Car("모하비", 2020),
new Car("아반떼", 2017),
new Car("아반떼", 2016),
new Car("아반떼", 2015);
));
return cars.findAllByCarName(carName);
}
}
해당하는 조건으로만 생성된 일급 컬렉션이 생겨서 비즈니스 로직에 의존하지 않고
순수 Car
리스트에 대한 테스트만 진행을 해볼 수 있게 되었다.
그리고 불변으로 만들어 주어야 무분별한 수정, 삽입이 안되게끔 만들어 줄 수 있다.
그러기 위해선 일급 컬렉션 객체에서 Getter
, Setter
같은 메소드들을 제외시키고,
오로지 생성자를 통한 생성, 별도로 구현한 로직외 값을 수정 불가 하게끔 정의해주고 사용한다.
이렇게 되면 일급 컬렉션 + 불변객체 가 성립이된다.
더 나아가서 지금은 차 브랜드를 현대, 기아 두개를 같이 넣었지만
KiaCars
, HyundaiCars
처럼 일급 컬렉션을 따로 만들어서 관리할 수도 있다.
이렇게 따로 만든채로 다른 비즈니스 로직을 수행하게끔 해줄 수도 있다.
하나 더 나아간다면 또 공통적으로 ~를 한다.
라는 기능이 존재할 경우 인터페이스를 놓고 구현해줌으로써
또 여러 과정을 거치는 방법도 있을 것이다.
이런 일급 컬렉션을 사용하는 것에 재미가 들렸고 완벽하게라고는 못하겠지만,
점차 쓰는 빈도가 늘어가면서 객체지향, 그리고 리팩토링에 불이 붙을것 같다.