spring 2기의 과정이 끝난지는 제법 지났지만, 아직 미션을 완료하지 못해 정리하며 진행해보려고 한다.
오늘은 받았던 피드백 중 일급컬렉션을 적용해보는 과정을 포스팅 해보려고 한다.
일급 컬렉션 객체를 도출하여 해당 객체에 비즈니스 로직의 책임을 위임해보자.
- 지하철 정보 관리 기능 미션을 진행하면서 구간이 추가 될 때의 비즈니스 로직을 서비스 레이어에서 처리하였는데
public SectionResponse save(Long lineId, SectionRequest request) {
Line line = lineService.findLineById(lineId);
Station upStation = stationService.findStationById(request.getUpStationId());
Station downStation = stationService.findStationById(request.getDownStationId());
List<Section> sections = sectionDao.findAllByLineId(line.getId());
if (!sections.isEmpty() && !sections.get(sections.size()-1).getDownStation().equals(upStation)) {
throw new IllegalArgumentException("추가하려는 상행역이 기존의 하행역과 불일치합니다.");
}
if (!sections.isEmpty() && lineService.findAllStation(sections).contains(downStation)) {
throw new IllegalArgumentException("이미 노선에 추가되어 있는 역입니다.");
}
Section section = sectionDao.insert(line.getId(), new Section(upStation, downStation, request.getDistance()));
return SectionResponse.of(section);
}
- 이 부분의 비즈니스 로직을 도메인 레이어로 분리해서 처리하면 좋을 것 같다는 피드백을 받아서
- 일급 컬렉션 객체를 도출해서 해당 객체에 비즈니스 로직의 책임을 위임하는 방법으로 처리해 보도록하려고 한다.
🤔 일급 컬렉션(First Class Collection)이란?
- 간단히 설명해 보자면 Collection 을 Wrapping 하여 그 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고 한다.
- 예시를 통해 살펴보도록 하자.
- 일급 컬렉션의 가장 큰 특징은 멤버 변수가 하나밖에 없다는 것이다.
public class Sections {
private List<Section> sections;
public Sections(List<Section> sections) {
this.sections = sections;
}
}
❗️직접 일급 컬렉션을 적용해보자
@Service
public class SectionService {
private final SectionDao sectionDao;
private final LineService lineService;
private final StationService stationService;
public SectionService(SectionDao sectionDao, LineService lineService, StationService stationService) {
this.sectionDao = sectionDao;
this.lineService = lineService;
this.stationService = stationService;
}
public SectionResponse save(Long lineId, SectionRequest request) {
Line line = lineService.findLineById(lineId);
Station upStation = stationService.findStationById(request.getUpStationId());
Station downStation = stationService.findStationById(request.getDownStationId());
List<Section> sections = sectionDao.findAllByLineId(line.getId());
if (!sections.isEmpty() && !sections.get(sections.size()-1).getDownStation().equals(upStation)) {
throw new IllegalArgumentException("추가하려는 상행역이 기존의 하행역과 불일치합니다.");
}
if (!sections.isEmpty() && lineService.findAllStation(sections).contains(downStation)) {
throw new IllegalArgumentException("이미 노선에 추가되어 있는 역입니다.");
}
Section section = sectionDao.insert(line.getId(), new Section(upStation, downStation, request.getDistance()));
return SectionResponse.of(section);
}
}
- save 메서드를 살펴보면, section 이 sections에 추가될 때 조건을 확인하게 되는데 ,
- save 메서드 뿐 만 아니라 sections 가 필요한 모든 장소에서는 이 검증 로직이 필요하게 된다.
- 만약 이 코드를 직접 작성한 당사자가 아니라면 이 검증 로직이 꼭 필요한 것인지 아닌지, 제대로 파악하지 못하는 문제가 발생할 수 있다.
- 이러한 문제를 해결하기 위해서는 해당 조건으로만 sections 가 생성될 수 있도록 자료구조를 만들어 해결 할 수 있다.
- 그렇게 만들어 진 클래스를 일급 컬렉션이라고 부른다.
public class Sections {
private List<Section> sections;
public Sections(List<Section> sections) {
this.sections = sections;
}
public void addValidate(Station upStation, Station downStation) {
if (!sections.isEmpty() && !sections.get(sections.size()-1).getDownStation().equals(upStation)) {
throw new IllegalArgumentException("추가하려는 상행역이 기존의 하행역과 불일치합니다.");
}
if (!sections.isEmpty() && findAllStation().contains(downStation)) {
throw new IllegalArgumentException("이미 노선에 추가되어 있는 역입니다.");
}
}
public List<Station> findAllStation() {
List<Station> stations = new ArrayList<>();
for (Section section : sections) {
stations.add(section.getUpStation());
stations.add(section.getDownStation());
}
return stations.stream().distinct().collect(Collectors.toList());
}
public int getSize() {
return sections.size();
}
public List<Section> getSectionList() {
return sections;
}
}
- 이제 sections 와 관련된 모든 로직들은 이 일급 컬렉션에서 수행할 수 있으며
public SectionResponse save(Long lineId, SectionRequest request) {
Line line = lineService.findLineById(lineId);
Station upStation = stationService.findStationById(request.getUpStationId());
Station downStation = stationService.findStationById(request.getDownStationId());
List<Section> sectionList = sectionDao.findAllByLineId(line.getId());
Sections sections = new Sections(sectionList);
sections.addValidate(upStation, downStation);
Section section = sectionDao.insert(line.getId(), new Section(upStation, downStation, request.getDistance()));
return SectionResponse.of(section);
}
- 해당 객체에 비즈니스 로직의 책임을 위임시킬 수 있다.
- 일급 컬렉션에 대해 더 자세히 알고싶다면 아래의 블로그를 참고해주세요.
- https://jojoldu.tistory.com/412