하나씩 차근차근
article thumbnail

앞에서 만든 OrderServiceImpl 구현체를 보면 다음과 같이 1000원을 할인해주는 FixDiscountPolicy 적용해서

사용하고 있습니다.

package hello.core.order;

public class OrderServiceImpl implements OrderService{
	
	private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
	...
}

만약 금액에 따른 할인을 하는  RateDiscountPolicy 를 적용하게 된다면 아래와 같이 OrderServiceImpl 을 수정해야합니다.

package hello.core.order;

public class OrderServiceImpl implements OrderService{

	// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
	private DiscountPolicy discountPolicy = new RateDiscountPolicy();
	...
}

이는 OrderServiceImpl 이 구현 클래스인 FixDiscountPolicy 와 RateDiscountPolicy 에 의존하고 있습니다.

이 코드는 새로운 기능을 확장해서 변경하게 되면 클라이언트 코드에 영향을 줘서 OCP 를 위반합니다.

때문에 구현 클래스가 아닌 인터페이스 (DiscountPolicy) 에만 의존하도록 아래와 같이 코드를 수정해야 합니다.

package hello.core.order;

public class OrderServiceImpl implements OrderService{

	private DiscountPolicy discountPolicy;
	...
}

이처럼 인터페이스에만 의존하도록 코드를 수정했는데, 이때 코드를 실행하면 DiscountPolicy 의 구현체가 없기 때문에 null pointer exception 이 발생합니다. 이 문제는 DiscountPolicy 의 구현체를 생성하고 주입해주는 새로운 클라이언트를 만들어서 해결할 수 있습니다.

 

AppConfig

OrderSerivcelmp 주입

위의 DiscountPolicy 의 구현체를 생성하고 주입해주기 위해 AppConfig 라는 설정 클래스를 만듭니다.

package hello.core;

import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.FixDiscountPolicy;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class Appconfig {
	
	public OrderService orderService() {
		return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
	}
}

AppConfig 에서 OrderServiceImpl 의 MemberRepository 와 DiscountPolicy 에 필요한 구현체를 생성합니다.

  • MemberRepository -> new MemoryMemberRepository()
  • DIscountPolicy -> new FixDiscountPolicy()

AppConfig 를 통해 생성한 객체 인스턴트를 OrderServiceImpl 에서 생성자를 통해 주입(연결) 합니다.

package hello.core.order;

import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;

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;
	}
	
	@Override
	public Order createOrder(Long memberId, String itemName, int itemPrice) {
		// TODO Auto-generated method stub
		
		Member member = memberRepository.findById(memberId);
		int discountPrice = discountPolicy.discount(member, itemPrice);
		
		return new Order(memberId, itemName, itemPrice, discountPrice);
	}
}

이렇게 코드를 수정하면 OrderServiceImpl 은 구현체인 FixDiscountPolicy 를 의존하지 않습니다.

때문에 OrderServiceImpl 입장에서는 생성자를 통해 어떤 구현체가 들어올지 알 수 없고,

AppConfig 에서 구현체를 FixDiscountPolicy 와 RateDiscountPolicy 두가지 중에 결정을 합니다.

 

MemberServiceImpl 주입

MemberServiceImpl 또한 MemoryMemberRepository() 구현체에 의존하고 있습니다.

package hello.core.member;

public class MemberServiceImpl implements MemberService {

	private final MemberRepository memberRepository = new MemoryMemberRepository();
	...
}

위와 동일하게 MemberServicelmpl 도 Appconfig 에서 구현체를 연결해주도록 수정을 해보겠습니다.

먼저 MemberServiceImpl 에 생성자를 통해 memverRepository 를 생성합니다.

package hello.core.member;

public class MemberServiceImpl implements MemberService {

	private final MemberRepository memberRepository;
	
	public MemberServiceImpl(MemberRepository memberRepository) {
		// TODO Auto-generated constructor stub
		this.memberRepository = memberRepository;
	}

	@Override
	public void join(Member member) {
		// TODO Auto-generated method stub
		memberRepository.save(member);
	}

	@Override
	public Member findMember(Long memberId) {
		// TODO Auto-generated method stub
		return memberRepository.findById(memberId);
	}
}

다음으로 Appconfig 에서 MemberService 를 통해 MemoryMeberRepositry 를 생성해서 MemberS

package hello.core;

import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.FixDiscountPolicy;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class Appconfig {
	
	public OrderService orderService() {
		return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
	}
	
	public MemberService memberService() {
		return new MemberServiceImpl(new MemoryMemberRepository());
	}
}

현재까지 결과를 다이어그램으로 나타내면 아래와 같습니다.

 

OrderServiceImpl 과 MemberServiceImpl 은 구현체 주입은 외부에 맡기고 실행만 하면 됩니다.

 

Appconfig 리팩터링

현재 Appconfig 는 new MemoryMemberRepository() 가 중복되어 사용되고 있습니다. 

만약 MemoryMeberRepositry 를 다른 구현체로 변경하게 될 경우 중복되는 부분을 모두 변경해야 합니다.

때문에 중복되는 부분을 제거하고 역할에 따른 구현이 보이도록 리팩터링 하겠습니다.

package hello.core;

import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.DiscountPolicy;
import hello.core.order.FixDiscountPolicy;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class Appconfig {
	
	public MemberRepository memberRepository() {
		return new MemoryMemberRepository();
	}
	
	public DiscountPolicy discountPolicy() {
		return new FixDiscountPolicy();
	}
	
	public OrderService orderService() {
		return new OrderServiceImpl(memberRepository(), discountPolicy());
	}
	
	public MemberService memberService() {
		return new MemberServiceImpl(memberRepository());
	}
}

 

결과

AppConfig 를 통해 애플리케이션을 사용 영역과 객체를 생성하고 구성(Configuration) 하는 영역으로 분리

만약 FixDiscountPolicy 에서 RateDiscountPolicy 로 바뀌어도 사용영역은 수정할 필요없이

AppConfig 의 discountPolicy() 안의 내용만 바꿔주면 됩니다.

package hello.core;

import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.DiscountPolicy;
import hello.core.order.FixDiscountPolicy;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import hello.core.order.RateDiscountPolicy;

public class Appconfig {
	
	public DiscountPolicy discountPolicy() {
		// return new FixDiscountPolicy();
		return new RateDiscountPolicy();
	}
	...
}

profile

하나씩 차근차근

@jeehwan_lee

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!