23.03.02

-봄

객체 지향 원칙 적용


AppConfig 리팩토링

– 더 나은 방향으로 문제 및 리팩토링

– AppConfig 코드의 이전 모습 복사역할에 따라 존재 구현이 잘 안됨


23.03.02 1

리팩토링

  • 프레스볼 클래스 X ‘상호 작용‘선택하다!

– AppConfig 리팩토링

public class AppConfig {

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

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

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

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

  • MemberService 역할, MemberRepository 역할 및 OrderService 역할이 모두 표시됩니다.

  • DiscountPolicy 역할이 추가되었습니다!
  • 장점: 각 역할을 알 수 있고(메소드 이름을 보면) 쉽게 변경할 수 있습니다.

  • new MemoryMemberRepository() → 이 부분의 중복 제거
  • MemoryMemberRepository → 다른 구현으로 변경 시 한 부분만 변경하면 됩니다.

  • AppConfig를 보면 역할과 구현 클래스가 한눈에 보이므로 전체 애플리케이션이 어떻게 구성되어 있는지 빠르게 확인할 수 있습니다.

새로운 구조 및 할인 정책 적용

– 상단으로 돌아가서 균일할인 정책을 균일(%)할인 정책으로 변경

– FixDiscountPolicy RateDiscountPolicy

– AppConfig만 변경하면 됩니다.

: 응용 프로그램이 큽니다.

응용 분야수업, 객체가 생성되고 구성되는 영역로 나누어


23.03.02 2

용도와 구성의 분리

  • 할인 정책이 변경되면 AppConfig 부분만 변경하면 됩니다.

  • 또한 FixDiscountPolicy → RateDiscountPolicy로 이동하면 구성 영역만 영향을 받고응용 분야는 전혀 영향을 받지 않습니다.

public DiscountPolicy discountPolicy() {
    return new RateDiscountPolicy();
}
  • RateDiscountPolicy(); 그냥 수리!


23.03.02 3

수정 코드 테스트

  • 당신이 성공했는지 확인


23.03.02 4

주문 앱

  • OrderApp에서 main()을 테스트해보면 값이 잘 나오는 것을 확인할 수 있습니다.

할인 정책을 변경하더라도 이제 애플리케이션을 구성하는 역할을 하는 AppConfig만 변경하면 됩니다.

OrderServiceImpl 고객 코드를 포함하여 사용 영역에서 코드를 변경할 필요가 없습니다.

구성 영역은 물론 변경됩니다.

– 구성 역할을 하는 AppConfig를 Application이라는 공연의 주최자로 생각할 수 있습니다.

공연의 주최자는 공연에 관련된 모든 구현 객체를 알아야 합니다.

DIP 및 OCP 모두 준수

전체 흐름 정리

새로운 할인 정책 개발
– 다형성 덕분에 신규 정가 할인코드 추가 개발이 용이합니다.

할인 정책의 신규 적용과 문제점은?
– 새로 개발된 정액할인 정책을 적용하기 위해서는 주문서비스 구현인 고객코드도 변경되어야 합니다.


– 주문 서비스 클라이언트는 DisCountPolicy 인터페이스뿐만 아니라 구체적인 클래스인 FixDisCountPolicy에도 의존 → DIP 위반
주문 서비스 고객이 너무 많이 했습니다.

관심사 분리
– 과거 → 클라이언트가 의존하는 서버 구현을 직접 빌드하고 실행
– 프로모터: AppConfig 출시
– AppConfig는 애플리케이션의 전체 동작 방식을 설정(configuration)하기 위한 것으로, 구현 개체 만들기하다 연결에 대한 책임
– 이제부터 클라이언트 개체는 권한이 축소된 상태에서 역할 수행에만 집중합니다(책임이 명확해짐).

AppConfig 리팩토링
– 명확한 역할 분리 및 구성 정보 구현
– 역할이 잘 정의되어 있습니다.


– 중복 제거

새로운 구조 및 할인 정책 적용
– 균일할인제 → 균일할인제(%)로 변경됩니다.


– AppConfig의 출현으로 애플리케이션이 강력해지고 있습니다.

응용 분야그리고 객체 생성 및 구성 영역통해 분리

– 할인 정책을 변경하더라도 → AppConfig가 위치한 구성 영역을 살펴보기만 하면 되고, 사용 영역을 변경해야 함 x 고객 코드인 주문 서비스 코드도 변경되지 않음은 물론

좋은 객체지향 설계의 5가지 원칙 적용

예제에서 SRP, DIP, OCP 사용

– SRP 단일 책임 원칙(1클래스 1책임)

  • 클라이언트 개체(예: OrderService 등)는 직접 구현 개체를 생성, 연결 및 실행하는 데 서로 다른 책임이 있습니다.

  • 개인 책임의 SRP 원칙에 따라 기업 영역 분리
  • AppConfig는 구현 개체를 만들고 연결하는 역할을 합니다.

  • 클라이언트 개체는 실행만 담당합니다.

– DIP 종속성 역전 원칙(추상화에 의존, 구체화에 의존 X – 종속성 주입은 이 원칙을 따르는 한 가지 방법)

  • 새로운 할인 정책을 개발하여 적용하려면 클라이언트 코드도 변경 → 기존 클라이언트 코드(OrderServiceImpl)는 DIP를 따르고 DiscountPolicy 추상화 인터페이스에 의존하는 것처럼 보였지만 수정할인 정책 또한 구체적인 구현 클래스에 따라 다릅니다.

  • 고객 코드입니다 할인 정책 추상화 인터페이스에만 의존하도록 코드 수정 → 클라이언트 코드는 인터페이스만으로는 아무것도 할 수 없음
  • AppConfig 수정할인 정책 클라이언트 코드 대신 개체 인스턴스를 생성하여 클라이언트 코드에 종속성 주입 → DIP 원칙 준수, 문제 해결

– OCP(확장에는 소프트웨어 항목이 열려 있어야 하지만 변경에는 닫혀 있어야 함)

  • 다형성 사용, 클라이언트는 DIP 관찰
  • 애플리케이션을 사용 및 구성 영역으로 나누기
  • AppConfig 수정 종속성할인 정책 비율할인 정책 클라이언트 코드에 주입되므로 클라이언트 코드를 변경할 필요가 없습니다.

  • 소프트웨어 요소를 새롭게 확장해도 응용분야 변경은 완료!
    → 변경이 종료됨: 변경할 필요가 없음

IoC, DI, 컨테이너

– Inversion of Control IoC(Inversion of Control)

– 기존 프로그램 → 클라이언트 구현체는 자신이 필요로 하는 서버 구현체를 생성하고 연결하여 실행한다.


– 구현 객체는 프로그램 자체의 제어 흐름을 제어 – 개발자에게는 자연스러운 흐름

AppConfig 소개
– 구현 객체는 자신의 로직 실행만 담당 → 프로그램의 제어 흐름 AppConfig에서 가져옴
(OrderServiceImpl은 필요한 인터페이스를 호출하지만 어떤 구현 개체가 실행되는지 알지 못합니다.

프로그램에 대한 제어 흐름에 대한 주권: AppConfig에는)
– OrderServiceImpl도 AppConfig에 의해 생성됩니다.


– AppConfig는 OrderServiceImpl이 아닌 OrderService 인터페이스의 다른 구현 개체도 생성하고 실행할 수 있습니다.

주문 서비스 임플 몰라, 그냥 네 논리를 실행
IoC(Inversion of Control)는 프로그램의 제어 흐름이 직접이 아닌 외부에서 관리될 때 발생합니다.

내가 제어하고 호출하지 않지만 프레임워크가 대신 호출합니다.


컨트롤 역전 → 컨트롤 역전
AppConfig는 Impl(구현)을 선택합니다.

프레임워크와 라이브러리
내가 작성한 코드 제어, 대신 실행 → Framework ok (JUnit) 액자
내가 작성한 코드는 제어 흐름을 직접 담당합니다 도서관

– 중독 주사

– OrderServiceImpl은 DiscountPolicy 인터페이스에 따라 다름 → 어떤 구현이 실제로 사용되는지 알 수 없음
– 종속성: 정적 클래스 종속성 / 런타임에 결정됨 동적 개체(인스턴스) 종속성 관계 둘을 따로 생각하다

정적 클래스 종속성 : 클래스가 사용하는 import 코드만 봐도 쉽게 의존관계를 파악할 수 있고, 어플리케이션을 실행하지 않고도 분석 가능 → 클래스 코드만 열어서 사용 가능
– OrderServiceImpl이 MemberRepository 및 DiscountPolicy에 종속되어 있음을 알 수 있습니다.


이러한 종속성만 실제로 OrderServiceImpl에 어떤 개체가 주입되는지 알지 못합니다.


23.03.02 5

클래스 다이어그램


23.03.02 6

종속성 다이어그램


23.03.02 7

종속성 표시


23.03.02 8

의존

  • 종속성 확인 가능

동적 개체 인스턴스의 종속성
– 응용 프로그램이 실행될 때 실제로 생성된 개체 인스턴스에 대한 참조에 연결된 종속성

– DI(Dependency Injection): 애플리케이션 실행 시간(런타임)에 외부에서 실제 구현 객체를 생성하고 이를 클라이언트에 전달하여 클라이언트와 서버 간의 실제 종속 관계를 설정합니다.

– 객체 인스턴스를 생성하고 참조 값을 전달하여 연결

– 의존성 주입 사용 → 클라이언트 코드 변경 없이 클라이언트 호출 대상의 타입 인스턴스 변경 가능/ 정적 클래스 종속성을 변경하지 않고 동적 객체 인스턴스의 종속성을 쉽게 변경(애플리케이션 코드 변경 없음)

IoC 컨테이너, DI 컨테이너

– (AppConfig와 유사) 객체 생성 및 관리 시 링크 종속성기능: IoC 컨테이너 또는 DI 컨테이너
– 최근 DI 컨테이너/또는 어셈블러, 객체 팩토리 등으로 불리는 종속성 주입에 중점을 둡니다.


봄으로 전환

– 기존에는 순수 Java 코드로만 DI를 적용했지만 이제는 Spring을 사용하세요!

– AppConfig Spring 기반으로 수정

import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(
                memberRepository(), discountPolicy());
    }
    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}
  • @Configuration 주석 추가 – 설정 및 구성 정보 담당
  • 사용 중인 가져오기가 올바른지 항상 확인해야 합니다.

  • 모든 메소드는 Spring 컨테이너에 등록됩니다.

– MemberApp에 펜 컨테이너 적용

public class MemberApp {

    public static void main(String() args) {

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

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

        Member findMember = memberService.findMember(1L);
        System.out.println("new Member = " + member.getName());
        System.out.println("find Member = " + findMember.getName());
    }
}
  • 봄에는 모든 것이 ApplicationContext로 시작됩니다.

  • ApplicationContext: Spring Container – 모든 것을 관리(Object – @Bean)
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
  • 주석 기반 구성
  • AppConfig를 파라미터로 입력 AppConfig의 환경 설정 정보로 Spring은 @Bean에 속하는 메소드를 Spring 컨테이너에 등록하고 관리합니다.

applicationContext.getBean("memberService", MemberService.class);
  • (“이름”, 유형)
  • AppConfig에서 사용할 항목
  • 두 번째 녀석!
  • 기본적으로 메서드 이름으로 등록


23.03.02 9

시작 메인()

  • 위의 5개는 Spring에 의해 내부적으로 등록됩니다.

  • 아래 5개는 @Bean으로 등록되어 Spring 컨테이너에 등록된다.

  • 이름 = 키, 값 = 반환 값
  • 전. memberService = 키 / 새 MemberServiceImpl() – 개체 인스턴스 = 값

– 펜 컨테이너를 OrderApp에 적용

public class OrderApp {

    public static void main(String() args) {

        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);
//        System.out.println("order.calculatePrice = " + order.calculatePrice());

    }
}
  • MemberApp과 같은 코드 수정


23.03.02 10

  • 잘 작동하는 것을 확인할 수 있습니다

– 펜 용기?

– ApplicationContext = 스프링 컨테이너
– 기존에는 개발자가 AppConfig, DI로 직접 객체를 생성했으나 이를 Spring 컨테이너를 통해 사용
– 설정(구성) 정보로 @Configuration과 함께 AppConfig 사용
모든 @Bean 메소드를 호출하여 반환된 객체를 Spring 컨테이너에 등록 → Spring 컨테이너에 등록된 객체 이렇게 = 봄 콩
– 접두사 @Bean 메서드 이름두번째 스프링 빈 이름(memberService, orderService)로 사용
– 기존에는 개발자가 AppConfig로 직접 필요한 객체를 검색 → 스프링 컨테이너를 통해 필요한 스프링 빈(오브젝트)을 찾아야 합니다.


– 봄콩 applicationContext.getBean() 방법을 사용하여 찾을 수 있습니다
– Java 코드로 다 제가 직접 했어요 Spring Container에 Object를 Bean으로 등록 → Spring Container에서 Spring Bean을 찾아서 사용하도록 수정!
(@Autowired와 동일)

→ 코드가 더 복잡해진 것 같은데 장점은?


https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90 %EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/대시보드

Spring 핵심 원칙 – 기초 – 인프라 | 강의

Spring 초보자도 Spring의 핵심 원리를 이해하고 예제와 인프라를 통해 Spring의 기초를 강화할 수 있습니다.

www.inflearn.com

– (Spring Core Principles – Basic) 강의를 듣고 공부하려고 썼습니다.