
지난 시간에 OCP와 DIP 객체 지향 설계 원칙을 지키기 위해 구현체를 의존하는 것이 아닌 인터페이스를 의존하는 것으로 코드를 수정하였다.
하지만 구현체가 없는데 어떻게 구현할 수 있을까?
쉽게 생각해서 공연(프로그램)을 하는데 역할(인터페이스)와 배우(구현체)가 있다고 가정을 하자.
코드를 수정하기 전 인터페이스에도 의존하고 구현체에도 의존하는 코드였을때를 비유해서 말하자면,
🔍 주문 클래스 다이어그램
🔍 회원 클래스 다이어그램
남자주인공 역할(인터페이스)의 남자배우(구현체)가 여자 주인공역할(인터페이스)의 여자배우(구현체)도 캐스팅하는 것과 같다.
이와 같은 상황에서는 공연기획자(AppConfig)가 별도로 있어야하며, 공연기획자는 각 역할(인터페이스)의 배우(구현체)를 캐스팅 해야한다.
코드로 알아보자.
package hello.core.Order;
import hello.core.Discount.DiscountPolicy;
import hello.core.Discount.FixedDiscountPolicy;
import hello.core.Discount.RateDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
// private final MemberRepository memberRepository = new MemoryMemberRepository();
// private final DiscountPolicy discountPolicy = new FixedDiscountPolicy();
// private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
private DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Override
public Order createOrder(Long id, String name, int price) {
Member member = memberRepository.findById(id);
int discount = discountPolicy.fixedDiscount(member, price);
return new Order(id, name, price, discount);
}
}
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixedDiscountPolicy();
...
}
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
인터페이스만 호출한 뒤 생성자를 생성했다.
MemberServiceImpl 파일도 똑같이 수정해보자!
package hello.core.member;
public class MemberServiceImpl implements MemberService{
// private final MemberRepository memberRepository = new MemoryMemberRepository();
private MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findByMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
...
}
public class MemberServiceImpl implements MemberService{
private MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
package hello.core;
import hello.core.Discount.RateDiscountPolicy;
import hello.core.Order.OrderService;
import hello.core.Order.OrderServiceImpl;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
// 실제 동작에 필요한 구현 객체를 생성한다.
public class AppConfig {
// 주문 서비스
// OrderService(역할) OrderService(구현체)
// 역할에 어떤 구현체를 넣을 것인지?
public OrderService OrderService(){
return new OrderServiceImpl(new MemoryMemberRepository(), new RateDiscountPolicy());
}
// 멤버 서비스
// MemberService(역할) MemberService(구현체)
// 역할에 어떤 구현체를 넣을 것인지?
public MemberService MemberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
}
package hello.core;
import hello.core.Order.Order;
import hello.core.Order.OrderService;
import hello.core.Order.OrderServiceImpl;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
public class OrderApp {
public static void main(String[] args) {
// MemberService memberService = new MemberServiceImpl();
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.MemberService();
// OrderService orderService = new OrderServiceImpl();
OrderService orderService = appConfig.OrderService();
// 멤버 생성
Long id = 1L;
Member member = new Member(id, "홍길동", Grade.VIP);
memberService.join(member);
// 주문 생성
Order order = orderService.createOrder(1L, "새우깡", 1700);
// 주문 내용 출력
System.out.println(order);
System.out.println(order.calprice());
}
}
package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
public class MemberApp {
public static void main(String[] args) {
// MemberService service = new MemberServiceImpl();
AppConfig appConfig = new AppConfig();
MemberService service = appConfig.MemberService();
Member member = new Member(1L, "홍길동", Grade.VIP);
service.join(member);
Member member1 = service.findByMember(member.getId());
if(member1.equals(member)){
System.out.println("같은 객체 입니다");
}else{
System.out.printf("다른 객체 입니다");
}
}
}
package hello.core.member;
import hello.core.AppConfig;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MemberServiceImplTest {
// MemberService memberService = new MemberServiceImpl();
private MemberService memberService;
@BeforeEach
void config(){
AppConfig appConfig = new AppConfig();
memberService = appConfig.MemberService();
}
@Test
void join(){
// given
Member member = new Member(1L, "홍길동", Grade.VIP);
// when
memberService.join(member);
// then
assertEquals(member, memberService.findByMember(member.getId()));
Assertions.assertThat(member).isEqualTo(memberService.findByMember(member.getId()));
}
}

package hello.core.Order;
import hello.core.AppConfig;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class OrderServiceTest {
// MemberService memberService = new MemberServiceImpl();
// OrderService orderService = new OrderServiceImpl();
private MemberService memberService;
private OrderService orderService;
@BeforeEach
void config(){
AppConfig appConfig = new AppConfig();
memberService = appConfig.MemberService();
orderService = appConfig.OrderService();
}
@Test
void createOrder(){
Long id = 1L;
Member member = new Member(id, "홍길동", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(1L, "새우깡", 1800);
Assertions.assertThat(order.getDisPrice()).isEqualTo(180);
}
}

✅ DIP & OCP
✅ 스프링에서 항상 말했던 의존성 주입에 대해 이해하게 되었다.
✅ 설정파일에서만 구현체를 변경하면 되므로 코드수정에 용이하다.
✅ 객체 지향 설계 원칙 알아보기