개요
먼저 스프링을 공부하기 앞서 자바 공부와 OOP 공부를 병행하며 간단한 자바 코드를 객체지향 설계에 맞게 구현하려고 한다.
이젠 게시글에서 객체지향 프로그래밍은 역할, 구현을 분리한다.
이를 자바에 적용시키기 위해 역할-인터페이스, 구현-인터페이스를 구현한 클래스, 구현 객체로 나뉜다 했던 것을 생각하며 간단한 코드를 작성했다.
요구사항
비즈니스 요구사항과 설계
회원
- 회원을 가입하고 조회할 수 있다.
- 회원은 일반과 VIP 두 가지 등급이 있다.
- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)
주문과 할인 정책
- 회원은 상품을 주문할 수 있다.
- 회원 등급에 따라 할인 정책을 적용할 수 있다.
- 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있 다.)
- 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)
주문과 할인 정책의 요구사항을 보면 나중에 변결 될 수 있는 사항이 있다. 따라서 우리는 객체지향적 프로그래밍을 준수하며 유연하고 변경에 용이한 프로젝트를 구현할 필요가 있다.
설계
위와 같은 도메인을 구현하기 위해 주문 도메인은 다음과 같이 구성되어 있다.
할인 정책이 바뀔 수 있게끔 OrderServiceImpl이 인터페이스인 DiscountPolicy를 의존하게 구현하려 하였다.
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
따라서 할인 정책이 바뀌면 OrderServiceImpl에서 할인 정책을 바꾸면 된다.
위 코드를 분석해보자
1. 역할과 구현을 분리했다 -> OK
2. 다형성 활용 및 인터페이스와 구현 객체 분리 -> OK
따라서 객체 지향적 프로그래밍을 적용한 프로그램이다... 라고 보이지만 사실은 그렇지 않다.
문제점
OrderServiceImpl는 DiscountPolicy 인터페이스만 의존한다고 생각할 수 있는데 사실은 구현 인터페이스인 FixDiscountPolicy(또는 RateDiscountPolicy) 또한 의존하고 있다.
인텔리제이에서 지원하는 다이어그램에 dependency를 추가해서 확인해보자. 의존 관계를 쉽게 확인할 수 있다.
따라서 DIP를 위배했다.
다음으로 기능을 확장해서 할인 정책을 바꾸기 위해 클라이언트 코드를 변경해야 한다.
따라서 OCP를 위배했다.
그렇다면 어떻게 이를 해결할 수 있을까?
해결 방법
쉽게 생각하자. OrderServiceImpl에서 필요한 DiscountPolicy 할인 정책의 인스턴스를 new를 통해 생성하고 있기 때문에 발생한 문제이다.
이를 구성역역으로 빼내어 생성자를 주입하면 된다.
AppConfig라는 클래스를 생성하여 구현 객체를 생성하고 연결하는 역할(생성자 주입)을 책임지게 구현한다.
생성자 주입
클라이언트 영역에서의 기능을 변경하지 않고 구성 역역에서 필요한 생성자를 주입한다.
public class AppConfig {
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public DiscountPolicy discountPolicy() {
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
위에서 필요했던 생성자들을 AppConfig에서 주입 받아 사용할 수 있도록 개선한다.
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
다음과 같은 개선된 코드를 통해 할인 정책이 변경 되어도 클라이언트 영역을 변경하지 않고 구성을 담당하느 AppConfig 영역을 수정하면 된다.
'Study > OOP' 카테고리의 다른 글
프록시 패턴과 데코레이터 패턴 (1) | 2023.10.05 |
---|---|
IoC, DI, 그리고 컨테이너 (0) | 2023.07.25 |
객체 지향 특징과 SOLID 원칙 (0) | 2023.07.25 |