My Space

반응형

스프링 프레임워크의 핵심

  • 스프링은 자바 언어 기반의 프레임워크
  • 자바 언어의 가장 큰 특징 - 객체 지향 언어
  • 스프링은 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크
  • 스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크

스프링과 객체 지향

  • 다형성이 가장 중요하다.(객체지향의 추상화, 상속, 다형성, 캡슐화 중)
  • 스프링은 다형성을 극대화해서 이용할 수 있게 도와준다.
  • 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 이용해서 역할과 구현을 편리하게 다룰 수 있도록 지원한다.
  • 스프링을 사용하면 레고 블럭을 조립하듯이 구현을 편리하게 변경할 수 있다.

좋은 객체 지향 설계의 5가지 원칙(SOLID)

클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙 정리

  • SRP : 단일 책임 원칙 (한 클래스는 하나의 책임만을 가져야 한다)
  • OCP : 개방-폐쇄의 원칙 (소프트웨어 요소는 확장에는 열려있고, 변경에는 닫혀있어야 한다)
  • LSP : 리스코프 치환 원칙 (객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다)
  • ISP : 인터페이스 분리 원칙 (특정 클라이언트를 위한 인터페이스 여러개로 나누는게 좋다)
  • DIP : 의존관계 역전 원칙 (구현 클래스에 의존하지 말고, 인터페이스에 의존해야 한다)

정리

- 객체 지향의 핵심은 다형성

- 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수 없다.

- 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경된다.(OCP 위반)

- 다형성 만으로는 OCP, DIP를 지킬 수 없다.

 

※ ODC, DIP를 코드를 통해 이해하기

아래와 같은 코드들이 있을때,

OrderService : 주문 인터페이스

OrderServiceImpl : OrderService의 구현체

DiscountPolicy : 할인관련 인터페이스

FixDiscountPolicy : DiscountPolicy 구현체(정액 할인 정책)

RateDiscountPolicy : DiscountPolicy 구현체(정률 할인 정책)

 

OCP, DIP 위반 코드

//OrderServiceImpl.java
public class OrderServiceImpl implements OrderService {

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

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

//문제점
OCP 위반 : 할인 정책을 정액 -> 정률로 변경하려면 
new FixDiscountPolicy()를 new RateDiscountPolicy()로 변경해야한다.
소스 코드 수정 필요.

DIP 위반 : OrderServiceImpl.java 소스코드에 인터페스만 의존해야하는데 구현클래스도 의존하고있다.
private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); 이부분을 보면
인터페이스인 DiscountPolicy와 구현체인 FixDiscountPolicy를 의존하고 있음.

OCP, DIP 보완 코드(순수 자바 코드)

//AppConfig.java (구현 객체를 생성하고 연결 책임 클래스)
public class AppConfig {
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

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

//OrderServiceImpl.java
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;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

//OrderApp.java
public class OrderApp {

    public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();
        OrderService orderService = appConfig.orderService();
        
        Long memberId = 1L;
        Member member = new Member(memberId, "memberA", Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, "itemA", 20000);
    }
}

//설명
구현 객체를 생성하고 연결하는 책임을 가지는 AppConfig.java 클래스 별도 생성.
결국 AppConfig로 인해 서비스에서는 인터페이스만 의존하고 구현체의 변경에 대해서는
신경을 쓸 필요가 없다.

OCP 보완 : 할인 정책 변경시 서비스 코드를 건들필요 없이 AppConfig.java 소스 수정만으로 해결

DIP 보완 : OrderServiceImpl는 DiscountPolicy라는 인터페이스만 의존하고,
해당 인터페이스의 구현클래스가 무엇인지 알지못한다.

 

OCP, DIP 보완 코드(스프링 컨테이너를 사용한 코드)

//AppConfig.java (구현 객체를 생성하고 연결 책임 클래스)
@Configuration
public class AppConfig {
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}

//OrderApp.java
public class OrderApp {

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

        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);

        Long memberId = 1L;
        Member member = new Member(memberId, "memberA", Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, "itemA", 20000);

        System.out.println("order = " + order.toString());
        System.out.println("order.calculatePrice() = " + order.calculatePrice());
    }
}

// 설명
- ApplicationContext를 스프링 컨테이너라 한다.
- ApplicationContext는 인터페이스이다.
- 구현체는 new AnnotationConfigApplicationContext(AppConfig.class);
- 기존에는 개발자가 AppConfig를 사용해 직접 객체를 생성하고 DI를 했지만,
  이제부터는 스프링 컨테이너를 통해서 사용한다.

위와 같이 @Bean을 통해 스프링 컨테이너에 객체를 등록하고 애플리케이션 구동하면 아래와 같은 로그가 뜬다.

서버 구동시 bean에 담긴 객체

※ 참 고

이전에는 bean설정을 xml에서 하였지만 요즘엔 위와 같이 자바 @Configuration 코드로 설정한다.

이렇게 @Configuration코드로 하면 factoryBean을 통해 bean을 등록하는 방식이다.

또한, 스프링 컨테이너를 사용하면 기본적으로 객체를 모두 싱글톤으로 만들어서 관리해준다.

스프링의 IoC, DI

제어의 역전 IoC(Inversion of Control)

위의 OCP, DIP 보완 코드를 보면 OrderServiceImpl은 필요한 인터페이스만 호출하고 AppConfig가 프로그램의 제어 흐름을 가져간다. 

즉 OrderServiceImpl는 묵묵히 자신의 로직에만 신경쓰고 실행한다.

프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것이 제어의 역전(Ioc)이다.

 

※ 참 고(프레임워크 vs 라이브러리)

프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 프레임워크다.(JUnit)

내가 작성한 코드가 직접 제어의 흐름을 담당한다면 라이브러리다.

 

의존관계 주입 DI(Dependency Injection)

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

 

 

'Development > OTHER' 카테고리의 다른 글

URL과 URI의 차이  (0) 2021.08.12
애자일 소프트웨어 개발 선언  (0) 2021.07.29
OAuth/JWT  (0) 2021.07.24
기술면접  (0) 2021.05.04
clean code 3가지 사항  (0) 2021.04.29

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading