일급 컬렉션
일단 나는 넥스트스텝의 TDD, Clean Code with Java 12기를 하면서
여기서도 일급 컬렉션을 사용했었다.
참 웃겼던건 이것을 조금 응용을 했었어야 했는데 개념 자체도 자세하게 정리가 덜 된것 같았다.
도메인에서부터 차근차근 만들어가는 것에서는 어느정도 생각이 잘 들었지만,
기존 레거시 코드에 이런게 적용되어 있지 않고 뚱뚱하게 로직이 작성되어 있으면
그냥 넘어갔던게 흔했다.
객체지향 생활체조 원칙
소트웍스 앤솔러지에서 발췌된 객체지향 생활체조 원칙이다.
- 규칙 1: 한 메서드에 오직 한 단계의 들여쓰기만 한다.
- 규칙 2: else 예약어를 쓰지 않는다.
- 규칙 3: 모든 원시값과 문자열을 포장한다.
- 규칙 4: 한 줄에 점을 하나만 찍는다.
- 규칙 5: 줄여쓰지 않는다(축약 금지).
- 규칙 6: 모든 엔티티를 작게 유지한다.
- 규칙 7: 2개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
- 규칙 8: 일급 컬렉션을 쓴다.
- 규칙 9: 게터/세터/프로퍼티를 쓰지 않는다.
진하게 설정해 놓은 것들은 웬만하면 거의 적용하고 있는 규칙들이다.
단, 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
처럼 일급 컬렉션을 따로 만들어서 관리할 수도 있다.
이렇게 따로 만든채로 다른 비즈니스 로직을 수행하게끔 해줄 수도 있다.
하나 더 나아간다면 또 공통적으로 ~를 한다.
라는 기능이 존재할 경우 인터페이스를 놓고 구현해줌으로써
또 여러 과정을 거치는 방법도 있을 것이다.
마무리
이런 일급 컬렉션을 사용하는 것에 재미가 들렸고 완벽하게라고는 못하겠지만,
점차 쓰는 빈도가 늘어가면서 객체지향, 그리고 리팩토링에 불이 붙을것 같다.
'Java' 카테고리의 다른 글
Checked Exception, Unchecked Exception (0) | 2022.09.07 |
---|---|
변성 (0) | 2022.08.11 |
변수 (0) | 2022.08.07 |
상태 패턴 적용 (0) | 2022.08.07 |