인터페이스의 디폴트 메서드를 사용한 요금 정책 도메인 구현

·2023년 6월 5일
0

배경

지하철 미션과 장바구니 미션에서 요금 정책에 대한 설계가 필요했다.
요금 및 할인 관련은 바뀔 가능성이 높은 도메인이므로 매우 유연한 설계가 필요하다!!

기존 설계

요금 정책이 좀 더 복잡했던 지하철 미션을 예시로 들어서 바꿔보자.
우선 기존 내가 설계했던 방식은 아래와 같다.

요금 정책은 나이별, 요금별, 거리별로 있으므로 위와 같은 정보가 필요하다.

여러 개의 요금 정책을 체이닝으로 적용하기 위해서 위와 같은 인터페이스를 구현했다.

각 구현체들은 컴포넌트로 적용을 해서 calculate(FareInfo fareInfo) 메서드를 구현한다.

최종적으로 여러 FareStrategy를 가지고 모두 계산된 FareInfo를 리턴해주는 FareCalculator를 사용한다.

요금 정책들을 적용하는 순서를 보장해야 하므로 FareCalculator를 빈으로 등록해서 조립하는 객체를 만들었다.

사실 순서 보장을 위한 방법으로는 @Order를 사용해서 List<FareStrategy>로 자동 주입 받을 수도 있다.
하지만 우선 각 구현체를 스프링 빈으로 등록해야 한다. 또한 순서를 보기 위해서는 구현체별 어노테이션을 확인해야 하고, 런타임 때의 리플렉션을 통한 주입이 필요할까 싶다. 따라서 위처럼 직접 조립하는 방식을 채택했다.

기존 방식의 단점

지금처럼 구현을 했다면, 현재의 FareInfo의 정보만 필요한 정책이라면 얼마든지 구현체를 만들고, 조립하는 부분만 수정하면 된다.

그러나 필요한 정보가 많아져서 FareInfo가 비대해진다면?
간단한 정보만 필요할 때도 모든 정보를 주입 받아야 한다는 단점이 있다.

Consumer의 andThen()을 참고해서 다른 구조로 만들어보자!!


디폴트 메서드를 사용한 구현 방법

인터페이스는 위와 같다.
calculate()는 단순히 요금을 받아서 현재의 정책을 적용한 이후의 요금을 리턴한다.

핵심은 andThen() 메서드이다.
FareStrategy는 함수형 인터페이스이므로 람다식을 사용해서 간단하게 체이닝을 적용해줬다.
여러 구현체들을 원하는 순서대로 원하는 정책들을 적용할 수 있게 된 것이다.

구현체는 위와 같이 calculate() 메서드를 구현하고, 자신의 필드로 필요한 정보들을 생성자 주입받도록 한다.

최종적으로 원하는 정책들을 위와 같이 andThen() 체이닝을 통해 사용할 수 있다.

기존의 방식과 달라진 점은

  • 할인 정책마다 필요한 정보가 다를 때, 자신이 필요한 정보만 받아서 사용할 수 있다.
    • 사용할 때도 편리하고 테스트할 때는 더욱 간단하게 코드를 작성할 수 있다.
  • 그리고 원하는 정책만 골라서 유연하게 사용할 수 있다.

이 방식이 정답은 아니지만 이러한 방법도 있다는 것을 참고하면 좋겠다😎

profile
渽晛

0개의 댓글