[1.4~7] 용어정리 · Issue #169 · Java-Bom/ReadingRecord
추상 팩토리 패턴 (13ea7ab) 제어의 역전 (d248362) 빈 팩토리, 애플리케이션 컨텍스트, 컨테이너, IoC 컨테이너, 스프링의 빈 (8fd75ec} 오브젝트 동일성과 동등성, 싱글톤 레지스트리 (dca60a6) 싱글톤 패
github.com
추상팩토리패턴
연관된 객체를 생성하는 팩토리를 추상화함으로써 클라이언트가 어떤 객체가 생성되는지 알 필요가 없게 할 수 있는 패턴.
public interface CardFactory {
Card createCard();
CardApp createCardApp();
}
public class SamsungCardFactory implements CardFactory{
@Override
public Card createCard() {
/**
* 삼성카드만의 생성로직
*/
return new SamsungCard();
}
@Override
public CardApp createCardApp() {
/**
* 삼성카드 앱만의 생성로직
*/
return new SamsungCardApp();
}
}
/**
* 클라이언트 코드 내부 어디에도 구체클래스는 드러나지 않는다
*/
public class CardClient {
private final CardFactory cardFactory;
public CardClient(final CardFactory cardFactory) {
this.cardFactory = cardFactory;
}
public void useCard() {
Card card = cardFactory.createCard();
card.pay();
}
public void userCardApp() {
CardApp cardApp = cardFactory.createCardApp();
cardApp.use();
}
}
제어의 역전
생성에 대한 책임을 다른 객체(IoC 컨테이너)에게 위임하는 것.
클라이언트는 IoC 컨테이너를 통해 객체를 얻는다. UserService는 자신이 사용하는 인터페이스의 구현체에 대해 알 필요가 없으며 변경되어도 코드의 변경이 있지 않다. 높은 응집도와 OCP를 만족한다고 할 수 있다.
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
public class IoCContainer {
/**
* IoC: 제어의 역전
* UserService의 UserRepository 구현체에 대한 생성의 책임을 IoCContainer에 위임한다.
*
* @return
*/
public UserService javabomUserService() {
return new UserService(new JavabomUserRepository());
}
}
빈팩토리, 애플리케이션 컨텍스트, 컨테이너, IoC 컨테이너
모두 빈의 생명주기를 관리하는 애플리케이션 컨텍스트를 의미하는 말이다.
애플리케이션 컨텍스트의 장점
1) 클라이언트는 구체적인 팩토리 클래스를 알지 못해도 된다. 아래처럼 UserRepository 라는 인터페이스로 구현체를 받을 수 있다.
UserRepository userRepository = context.getBean("defaultUserRepository", UserRepository.class);
2) 종합 IoC 서비스를 제공해준다
- 싱글톤, 프로토타입, 세션, 등 다양한 스코프의 빈을 제공
3) 빈을 검색하는 다양한 방법제공
// 이름으로 검색
UserRepository userRepository = context.getBean("defaultUserRepository", UserRepository.class);
UserRepository defaultUserRepository = context.getBean(DefaultUserRepository.class); // 타입으로
- @Service, @Component, @Repository, @Controller 등 애너테이션으로도 빈을 검색한다.
싱글톤 패턴
싱글톤 패턴은 항상 동일성을 만족하는 인스턴스를 반환하는 패턴이다.
싱글톤 패턴의 문제는 다음과 같다.
- 지저분한 코드
- JVM 환경에 따라 실제로는 싱글톤이 아닐 수도 있음
- 상속불가, static 필드 -> 객체지향 개념을 적용할 수 없음
- Mock 인스턴스 생성 불가. 테스트 불가.
싱글톤 패턴을 직접 구현한 UserDao
public class UserDao {
private static final Object LOCK = new Object();
private static UserDao INSTANCE;
private final Connection connection;
public UserDao(Connection connection) {
this.connection = connection;
}
/**
* UserDao 객체의 책임은 User모델을 다루는 로직들로만 응집되어있어야하는데
* 책임과 전혀 무관한 싱글톤만을 위한 코드가 들아간다.
*/
public static UserDao getInstance(Connection connection) {
synchronized (LOCK) {
if (Objects.isNull(INSTANCE)) {
INSTANCE = new UserDao(connection);
}
}
return INSTANCE;
}
public User getUser() {
// connection 에서 user얻어옴
Object result = connection.execute("select * from user");
return (User) result;
}
}
오브젝트의 동등성과 동일성, 싱글톤 레지스트리
동등성은 equalsTo로 비교한 결과(실제로 같은 오브젝트가 아닐 수 있음)
동일성은 == 으로 비교한 결과(실제로 같은 오브젝트)
@DisplayName("IoC컨테이너는 싱글톤 레지스트리로서의 역할을 한다")
@Test
void singletonRegistry() {
ApplicationContext context = new AnnotationConfigApplicationContext(ChapterOneConfiguration.class);
UserRepository user1 = context.getBean("defaultUserRepository", UserRepository.class);
UserRepository user2 = context.getBean("defaultUserRepository", UserRepository.class);
// 빈의 스코프: 프로토타입
UserRepository user3 = context.getBean("prototypeUserRepository", UserRepository.class);
/*
컨테이너에 의해 생성되는 오브젝트는 동일성을 만족한다.
즉, 컨테이너는 빈의 생명주기를 관리함과 동시에 싱글톤 오브젝트를 반환하는 싱글톤 레지스트리로서의 역할을 한다.
이때 빈의 스코프는 싱글톤
*/
assertThat(user1).isEqualTo(user2); // 동등성 (equlasTo), 동일한 정보를 담고있다.
assertThat(user1 == user2).isTrue(); // 동일성, 완전히 동일한 오브젝트이다
assertThat(user1 != user3).isTrue(); // 동일성을 만족하지 않는다.
}
빈의 스코프
- 싱글톤: 계속 같은 인스턴스를 반환 기본 설정
- 프로토타입: 얻을때마다 다른 인스턴스 반환
- 요청스코프: Http 요청시마다 새로운 인스턴스 반환
- 세션스코프: 세션이 유지될 때는 같은 인스턴스 반환. 새로운 세션의 요청에는 새로운 인스턴스 반환
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public UserRepository prototypeUserRepository() {
return new DefaultUserRepository();
}
// Http 요청 올때마다 새로 만드는 Request Scope
@Bean
@RequestScope
public UserRepository requestUserRepository() {
return new DefaultUserRepository();
}
@Bean
@SessionScope
public UserRepository sessionUserRepository() {
return new DefaultUserRepository();
}
DI의 세가지 조건
1) 인터페이스에 의존함으로써 느슨한 결합을 가져야한다.
/*
이 코드에서 DefaultUserRepository와의 의존관계는 드러나지 않는다
*/
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void register(User user) {
}
}
2) 런타임 시점의 의존관계는 컨테이너나 팩토리같은 제 3의 존재가 결정한다
3) 사용할 오브젝트에 대한 레퍼런스는 외부에서 주입(생성자, Setter,,)된다.
/*
UserService 는 이 설정 파일을 읽을 때(런타임)에 DefaultUserRepository 와 런타임 의존관계를 가진다.
여기서 DefaultUserRepository를 의존 오브젝트라고한다.(실제 사용대상의 오브젝트)
*/
@Bean
public UserService userService() {
return new UserService(defaultUserRepository());
}
스프링이 제공하는 DI와 DL의 차이
[1.4~7] IOC 와 DI, DL · Issue #173 · Java-Bom/ReadingRecord
Ioc di dl 의 차이를 정리하면 좋을거 같아
github.com
DI
IoC 컨테이너에 의해 구현체를 주입받는다. 위의 DI의 세가지 조건을 만족한다
@Bean
public UserService userService() {
return new UserService(defaultUserRepository());
}
DL
스프링이 제공하는 DL 기능은 getBean을 통해 빈을 검색하는 기능이다. 빈의 이름, 타입등으로 검색할 수 있다.
생성자나 Setter를 사용한 의존 주입을 하지 않을 때, 또는 DI 받으려는 객체가 빈이 아닐 때 활용할 수 있다. (DI를 받기 위해서는 자기 자신도 빈이어야 하기 때문)
UserRepository userRepository = context.getBean("defaultUserRepository", UserRepository.class);
UserRepository defaultUserRepository = context.getBean(DefaultUserRepository.class); // 타입으로
DI 장점 - 관심사의 분리 측면
DI를 이용하면 기존의 구현체에 기능추가 등 변경이 요구되어도 코드의 변경 없이 해결할 수 있다.
https://github.com/Java-Bom/ReadingRecord/issues/174#issuecomment-723386158
[1.4~7] DI 장점 · Issue #174 · Java-Bom/ReadingRecord
125P 에 DI 장점, 관심사의 분리에 대해서 나오는데 이거 엄청 중요한거 같아. 예제랑 자세한 설명추가해줘
github.com
'Reading Record > 토비의 스프링 3.0' 카테고리의 다른 글
[토비의 스프링 5장] 5.2 서비스 추상화 (0) | 2021.01.21 |
---|---|
[토비의 스프링 3장] 템플릿 3.5 ~ 3.7 (0) | 2020.12.13 |
1.8 ~ 2.2 (0) | 2020.11.27 |