본문 바로가기
Frameworks/Springboot

[Springboot] 객체 지향 설계를 위한 AppConfig 구성

by 젊은오리 2022. 7. 22.
728x90

[김영한님의 스프링 핵심 원리-기본편을 학습 후 정리한 내용입니다.]

스프링 핵심원리 강의를 듣던 중 다형성을 최대한 활용하여 프로그래밍을 하는 과정을 보면서.. 간단해 보이는 예제였음에도 불구하고 매우매우 잘 설계했다는 것이 느껴졌다. 스프링이 지원하는 기능을 사용하지 않고 자바코드만으로 좋은 객체지향 5원칙 SOLID를 따르도록 설명해주시는 교수님 강의 덕분에 스프링을 사용하는 근본적인 이유에 대해서 보다 잘 이해할 수 있었다. 물론  현재 대부분의 개발자들은 @Configuration을 사용해서 스프링빈에 등록하지만 실제로 AppConfig를 만들어보면서 객체지향의 대단함을 느낄 수 있었다!!

 

전체흐름을 구성한 클래스 다이어그램은 다음과 같다. OrderService라는 주문서비스 인터페이스를 구현한 클래스인 OrderServiceImpl에서 회원, 할인 정책에 대해 구성한 인터페이스인 MemberRepository, DiscountPolicy를 생성하면서, 거기에서 생기는 OCP, DIP원칙을 위반하는 문제점들을 해결해 나가는 것이 핵심이다. 더불어 한 클래스는 하나의 핵심 기능을 다루어야 한다는 SRP의 원칙도 지켜지는 셈이다.

 

문제발생>>

OrderServiceImpl에서는 다음과 같이 인터페이스(MemberRepository, DiscountPolicy)와 구현체(MemoryMemberRepository, FixDiscountPolicy) 동시에 의존하고 있다. (DIP위반

public class OrderServiceImpl implements OrderService{

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

 

또한 할인정책이 고정할인정책에서 정율할인정책으로 바뀐다고 했을 때 다음과 같이 코드수정이 일어난다.(OCP위반)

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy(); // RateDiscountPolicy()로 수정

 

 

해결>> Appconfig로 해결하고, 리팩토링으로 코드를 수정하자!

이러한 문제를 해결하기 위해 AppConfig 클래스를 정의해서 orderService()메서드를 통해서 우리가 원하는 구현클래스를 생성자를 통해 만들어준다. 

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

 

당연히 OrderServiceImpl클래스에는 회원과 할인정책을 받는 생성자가 정의되어 있어야 한다. 

코드를 살펴보면, 기존에 인터페이스와 구현클래스를 의존했던 것이 인터페이스만을 의존하게 수정되었고,(DIP 만족) 우리가 할인정책을 바꾸고자 할 때는 클라이언트코드(OrderServiceImpl)단에서 수정 없이 AppConfig클래스에서 코드를 수정하면 된다.

Appconfig클래스에서 정의한 orderService()메서드가 호출되면 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를 보면 지금은 보기 쉽게 OrderService위주로 정리하기 위해서 메서드가 한개뿐이지만, 실제로는 중복호출되는 클래스가 있다. 따라서 중복을 제거하고, 역할에 따른 구현이 잘 보이도록 리팩토링을 해보자.

아래 코드와 같이 OrderServiceImpl의 인자로 들어갈때 생성했던 클래스를 각각 메서드로 뽑아내었다. 이제 MemoryMemberRepository와 discountPolicy를 다른 구현체로 변경할 때 한 부분만 변경하면 된다.

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

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

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

 

728x90

댓글