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;
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(
new MemoryMemberRepository(),
new FixDiscountPolicy());
}
}
-이 코드는 Appconfig 코드로 감독자의 역할을 하는 클래스이다
멤버 서비스를 반환할 때 MemoryMemberRepository를 사용하는 MemberServiceImpl을 반환하는 등의 역할
문제점이 존재한다
new MemoryMemberRepository() 코드가 중복된다는 점이 문제점이다
이는 나중에 다른 MemberRepository로 변경할 때 2번 바꿔야 한다는 문제점이 존재한다
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
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;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(getMemberRepository());
}
public static MemoryMemberRepository getMemberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(getMemberRepository(), getDiscountPolicy());
}
public static DiscountPolicy getDiscountPolicy() {
return new FixDiscountPolicy();
}
}
그래서 AppConfig를 위와 같이 변경하였다.
이제 만약 FixDiscountPolicy에서 RateDiscountPolicy로 변경한다면
getDiscountPolicy 함수만 변경하면 된다
위와 같이 사용 영역의 코드는 변경할 필요 없고 구성 영역만 변경하며 확장 가능하다
OCP원칙을 잘 지키게 된다!
정리
**새로운 할인 정책 적용과 문제점**
새로 개발한 정률 할인 정책을 적용하려고 하니 **클라이언트 코드
**인 주문 서비스 구현체도 함께 변경해야함
주문 서비스 클라이언트가 인터페이스인 `DiscountPolicy` 뿐만 아니라, 구체 클래스인 `FixDiscountPolicy`
도 함께 의존 **DIP 위반
애플리케이션을 하나의 공연으로 생각
기존에는 클라이언트가 의존하는 서버 구현 객체를 직접 생성하고, 실행함
비유를 하면 기존에는 남자 주인공 배우가 공연도 하고, 동시에 여자 주인공도 직접 초빙하는 다양한 책임을 가지
고 있음
공연을 구성하고, 담당 배우를 섭외하고, 지정하는 책임을 담당하는 별도의 '공연 기획자'가 나올 시점
공연 기획자인 AppConfig가 등장
AppConfig는 애플리케이션의 전체 동작 방식을 구성(config)하기 위해, '구현 객체를 생성'하고, '연결'하는 책임
이제부터 클라이언트 객체는 자신의 역할을 실행하는 것만 집중, 권한이 줄어듬(책임이 명확해짐)
좋은 객체 지향 설계의 5가지 원칙
1.SRP 단일 책임 원칙
한 클래스는 하나의 책임만 가져야 한다
2.DIP 의존관계 역전 원칙
추상화에만 의존해야지 구체화에 의존하면 안된다
3.OCP
확장에는 열려 있으나 변경에는 닫혀 있다
소프트웨어 요소를 새롭게 확장해도 사용 영역의 코드를 변경할 필요가 없다
구성하는 영역의 코드만 변경하면 된다
IOC 제어의 역전
프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전이라고 한다.
AppConfig가 등장한 후 IOC가 적용된 것이다.
DI 의존성 주입
정적인 의존 관계란?
코드 상으로 import를 보고 판단할 수 있는 의존관계
동적인 의존 관계
애플리케이션 실행 시점이 되어서야 알 수 있는 의존 관계
AppConfig로 인해 애플리케이션 실행 전까지 FixDiscountPolicy가 사용될지 RateDiscountPolicy가 사용될지 모르는 의존 관계를 의미한다
AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 DI 컨테이너(IOC 컨테이너)
연결해주고 감독하는 역할을 하는 것은 DI 컨테이너(의존성 주입 컨테이너)라고 생각하면 된다.
이를 스프링 프레임워크로 전환하자
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
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;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(getMemberRepository());
}
@Bean
public static MemoryMemberRepository getMemberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(getMemberRepository(), getDiscountPolicy());
}
@Bean
public static DiscountPolicy getDiscountPolicy() {
return new FixDiscountPolicy();
}
}
@Configuration -> AppConfig에 설정을 구성한다는 의미
@Bean -> 스프링 컨테이너에 해당 메소드들을 등록하는 역할을 한다.
package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MemberApp {
public static void main(String[] args) {
// AppConfig appConfig = new AppConfig();
// MemberService memberService = appConfig.memberService();
// MemberService memberService = new MemberServiceImpl();
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());
}
}
기존에 Appconfig를 불러내서 이를 통해 서비스를 할당하는 식으로 했다면
이제는 스프링 컨테이너에서 빈을 꺼내서 서비스를 등록한다.
package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.order.Order;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
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("orer = " +order);
System.out.println("orer.calculatePrice = " +order.calculatePrice());
}
}
OrderApp도 위와 같아진다
ApplicationContext를 스프링 컨테이너라 한다
기존에는 AppConfig를 사용했지만 이제는 스프링 컨테이너를 통해서 사용한다.
스프링 핵심 원리 이해 (0) | 2025.04.01 |
---|---|
객체 지향 설계와 스프링 (0) | 2025.03.25 |
댓글 영역