SPRING

[스프링 핵심 원리] 객체 지향 설계 원칙(SOLID) / IoC / DI / DI 컨테이너

merryna 2022. 9. 26. 09:43
반응형

 

public class OrderServiceimpl implements OrderService{
    private final MemberRepository memberRepository = new MemoryMemberRepositroy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}

 


  • 단일책임원칙(SRP) : 한 클래스는 하나의 책임만 가져야 한다.

클라이언트 객체는 직접 구현 객체 생성, 연결, 실행 중

 

🍰 관심사 분리 필요 : AppConfig에서 구현 객체 생성 및 연결하는 책임

 


  • 의존관계 역전 원칙(DIP) : 구체화가 아닌 추상화에 의존해야 한다.

OrderServiceimpl이 추상화 인터페이스(DiscountPolicy)에 의존하는 것은 맞지만 구체화 구현 클래스(RateDiscountPolicy)에도 의존 중

 

public class OrderServiceimpl implements OrderService{
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}

🤢[NullPointException 발생!] 클라이언트 코드가 추상화 인터페이스에만 의존하도록 코드를 변경하면 클라이언트 코드는 인터페이스만으로는 아무것도 실행할 수 없음

 

🍰 클라이언트에 의존 관계 주입 : 클라이언트 코드 대신 AppConfig가 RateDiscountPolicy 객체 인스턴스 생성


  • 개방 폐쇄 원칙(OCP) : 확장에는 열려 있으나 변경에는 닫혀 있다.

기존에는 클라이언트 소스 코드를 변경하여 어떠한 구체화 구현 클래스를 의존할지 지정해주었음


🍰 애플리케이션을 사용 영역과 구성 영역으로 나누기 : 고정 할인 정책에서 정률 할인 정책으로 바뀌었을 경우 AppConfig에서 FixDiscountPolicy > RateDiscountPolicy으로만 수정하면 AppConfig가 의존관계를 변경해서 클라이언트 코트에 주입함. 구성 영역에서 처리하므로 사용 영역인 클라이언트 코드 수정이 불필요함.

 

*다형성 사용하고 클라이언트가 DIP를 지킴으로서 OCP를 지키기 좋은 코드가 됨.


 

🎂 SRP / DIP / OCP를 준수하여 수정한 코드

 

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;
    }
    ...
 }

추상화 인터페이스에만 의존하고 생성자를 통해 의존 관계 주입

> OrderServiceImpl 은 필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지 모른다.

 

AppConfig

public class AppConfig {

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

    private static MemoryMemberRepositroy memberRepository() {
        return new MemoryMemberRepositroy();
    }

    public OrderService orderService() {
        return new OrderServiceimpl(memberRepository(), new RateDiscountPolicy());
    }

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

 

  • 제어의 역전 IoC(Inversion of Control) : 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것

 

기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행했다.
프로그램의 제어 흐름은 AppConfig가 담당하고 관리함 (OrderServiceImpl도 AppConfig가 생성함) 

 

프레임워크 vs 라이브러리
프레임워크 : Appconfig나 프레임워크에 해당. 내가 작성한 코드를 제어하고, 대신 실행 (JUnit)
라이브러리 : 내가 작성한 코드가 직접 제어의 흐름을 담당

 

  • 의존관계 주입 DI(Dependency Injection)


- 정적인 클래스 의존관계
정적 클래스 의존관계는 실행없이 import 코드만 보고 의존관계를 쉽게 파악 가능

OrderServiceImpl ➡ MemberRepository / DiscountPolicy 

 

실제 어떤 객체가 OrderServiceImpl 에 주입 될지 알 수 없고 동적인 객체 인스턴스 의존 관계를 봐야함

 

 

- 동적인 객체 인스턴스 의존 관계
애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존 관계

애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서
클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입(DI)이라 한다.

정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 변경할 수 있다.

즉, 애플리케이션 소스를 수정하지 않고 Rate냐 Fixed나 수정 가능하다는 뜻

 

객체 인스턴스를 생성하고, 그 참조값을 전달해서 연결

 

public class OrderServiceimpl implements OrderService{
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}

 

> memberRepository, discountPolicy의 인스턴스의 참조값(MemoryMemberRepositroy/RateDiscountPolicy)으로 구성되어 있음

 

  •  IoC 컨테이너, DI 컨테이너(어샘블러, 오브젝트 팩토리)

AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것

 

 

 

 

출처 : 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

반응형