본문 바로가기

Development/Spring Boot

[Spring Boot] AppConfig 리팩토링

1- 리팩토링 대상 코드

package hello.core;

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

/**
 * Application 전체를 구성하고 설정하는 역할
 *  - 로미오 줄리엣 공연 예시 중 기획자
 *  - 어플리케이션에 대한 환경 구성을 모두 이곳에서 함
 */
public class AppConfig {

    // 생성자 주입: 생성자를 통해서 객체가 주입되는 것
    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

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

}

 

  • 하늘이가 예상 해보는 리팩토링!
package hello.core;

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

/**
 * Application 전체를 구성하고 설정하는 역할
 *  - 로미오 줄리엣 공연 예시 중 기획자
 *  - 어플리케이션에 대한 환경 구성을 모두 이곳에서 함
 */
public class AppConfig {

    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

    public AppConfig() {
        this.memberRepository = new MemoryMemberRepository();
        this.discountPolicy = new FixDiscountPolicy();
    }

    // 생성자 주입: 생성자를 통해서 객체 주입
    public MemberService memberService() {
        if (this.memberRepository == null)
            this.memberRepository = new MemoryMemberRepository();
        return new MemberServiceImpl(this.memberRepository);
    }

    public OrderService orderService() {
        if (this.discountPolicy == null)
            this.discountPolicy = new FixDiscountPolicy();
        return new OrderServiceImpl(this.memberRepository, this.discountPolicy);
    }

}

(1) 메모리 관점

- 인스턴스가 생성될때마다 기존에는 생성자 안에서 의존성 주입되는 인스턴스를 생성하여 넘겼었음

- 어차피 동일한 역할을 하는 것이기에 AppConfig 생성자를 통해 인스턴스 생성시, 주입되는 객체의 인스턴스를 생성해여 공통적으로 사용하면 메모리의 불필요한 낭비 필요 없이 가능할 것이라 생각.

 

(2) 유지보수 관점 (중복 코드)

- 덧붙여 이전 코드의 경우 MemberServiceImpl과 OrderServiceImpl 인스턴스 생성시, MemoryMemberRepository를 공통적으로 생성했는데,

- 만약 회원 저장소를 메모리가 아니라 자체 DB 혹은 외부 무언가로 변경하게 될 경우 각 구현체를 변경해야함.

- 하지만 만약 하늘쓰가 리팩토링한 것처럼 AppConfig 생성자에서만 MemberRepository의 구현체 생성을 수행한다면, 이후 다른 회원 저장소로 변경된다할 경우 해당 코드 한 줄만 변경하면 됨.

 

--> 생각 변화! 메모리 관점에서 오히려 저게 더 악수임. 필요할때만 생성하는 것이 오히려 메모리 낭비가 없기에 아래 강의 필기의 예시 코드처럼 리팩터링 하는 것이 장점이 더 강함.

 

2- 강의 필기

- 기존 코드 내에 중복이 존재하며, 역할에 따른 구현이 안보임

- IntelliJ는 자동으로 중복된 코드를 바꿀 수 있음

  Extract / Introduce -> Extract Method

   --> Mac 단축키: 옵션 + 커멘드 + M

 

- 리팩토링 코드

package hello.core;

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

/**
 * Application 전체를 구성하고 설정하는 역할
 *  - 로미오 줄리엣 공연 예시 중 기획자
 *  - 어플리케이션에 대한 환경 구성을 모두 이곳에서 함
 */
public class AppConfig {

    // 생성자 주입: 생성자를 통해서 객체가 주입되는 것
    // 중복된 코드 잡아주는 단축키 (맥 기준) : 옵션 + 커맨드 + M
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    private MemoryMemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

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

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

}

 

  • 장점

    - 메서드 역할을 명확히 알 수 있음
    - 구현체가 변경될 경우 코드 수정이 용이함
    - 어플리케이션 구성을 수비게 파악할 수 있음