객체 지향 원리 적용(2)

jooog·2022년 3월 31일
0

💡 기존 코드의 객체 지향 원리 위반

기존 코드는 OrderServiceImpl에서 인터페이스 뿐만 아니라 구체 클래스에도 의존하고 있었기 때문에 객체 지향의 원리에 어긋났었다. 이번에는 그 코드를 객체 지향의 원리에 따라변경해보며 스프링 프레임워크의 기능을 자세히 살펴보려고 한다.

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private DiscountPolicy discountPolicy = FixDiscountPolicy();
}

💡 생성자 주입

구현 객체를 생성하고 연결하는 별도의 AppConfig 클래스를 만들어본다. AppConfig 클래스는 별도의 환경 설정에 관한 모든 기능을 담당한다.

[AppConfig]

public class AppConfig {

    public MemberService memberService(){
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

    public OrderService orderService(){
        return new OrderServiceImpl(new MemoryMemberRepository(), new RateDiscountPolicy());
    }
}

[MemberServiceImpl]

public class MemberServiceImpl implements MemberService{

    private final MemberRepository memberRepository;


    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

[OrderServiceImpl]

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에서 구현 객체를 생성하고 생성한 객체 인스턴스의 참조를 생성자를 통해서 주입해주는 방식으로 코드를 변경했다. 설계 변경으로인해 MemberServiceImpl, OrderServiceImpl은 더이상 구현 객체에 의존하지 않으며 어떤 구현 객체를 주입할지는 AppConfig에서 생성자를 통해 결정된다.

MemberServiceImpl, OrderServiceImpl은 추상에만 의존하면 되기때문에 객체를 생성하고 연결하는 역할과 실행이 명확히 분리되었다.

💡 DI(Dependency Injection)


🎈 MemberServiceImpl 입장에서는 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection) 즉, 의존관계 주입 또는 의존성 주입이라 한다.

[test 해보기]


//MemberApp
public class MemberApp {

    public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();
}


//OrderApp
public class OrderApp {

    public static void main(String[] args) {

        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();
        OrderService orderService = appConfig.orderService();}

[MemberServiceTest]

public class MemberServiceTest {

    MemberService memberService;

    //테스트가 실행되기 전 무조건 실행된다.
    @BeforeEach
    public void beforeEach(){
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
    }

    @Test
    void join(){
        //given
        Member member = new Member(1L,"memberA", Grade.VIP);

        //when
        memberService.join(member);
        Member findMember = memberService.findMember(1L);

        //then
        Assertions.assertThat(member).isEqualTo(findMember);

    }
}

AppConfig를 통해서 관심사를 분리했으며 AppConfig에서 구체 클래스를 선택하고 각 클래스는 기능을 실행하는 책임만 지면 된다.

💡 AppConfig 리팩토링

AppConfig를 보면 중복이 있고 역할에 따른 구현이 잘 안보인다. 이 코드를 리팩토링 해본다.

[수정 전 AppConfig]

public class AppConfig {

    public MemberService memberService(){
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

    public OrderService orderService(){
        return new OrderServiceImpl(new MemoryMemberRepository(), new RateDiscountPolicy());
    }
}

[AppConfig 리팩토링]

public class AppConfig {

    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }

    public DiscountPolicy discountPolicy(){
        return new FixDiscountPolicy();
    }

    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }

    public OrderService orderService(){
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
}

중복되던 new MemoryMemberRepository() 부분을 제거하고 역할과 구현 클래스가 한눈에 파악될 수 있도록 코드를 수정했다.

이 글은 김영한님의 스프링 핵심 원리 강의를 듣고 정리한 내용입니다.

0개의 댓글