티스토리 뷰
클래스의 의존 관계
많은 클래스들은 하나 이상의 자원에 의존한다. 자원에 의존해 기능을 수행하는 클래스는 의존성(dependency) 을 갖는다. 라고 표현할 수 있다. 이러한 클래스의 의존성은 보통 의존하고자 하는 기능을 가진 클래스의 참조변수를 멤버변수로 선언함으로써 표현한다.
public class AccountService {
private TransferService transferService;
public void transfer(Account withdrawalAccount, Account depositAccount) {
// business logic start ...
}
}
위 예시에서 계좌 서비스는 송금 서비스에 의존한다. (계좌 서비스의 기능 구현에는 송금 서비스의 도움이 필요하다.)
사용하는 자원에 따라 동작이 달라지는 클래스
사용하는 자원에 따라 동작이 달라지는 클래스의 구현방법으로 정적 유틸리티 클래스나, 싱글턴 방식은 적합하지 않다. 가령 위와 같은 예시처럼 계좌 서비스가 송금 서비스에 의존할 때, 이를 정적 클래스로 구현했다고 가정해보자.
public class AccountService() {
private static final TransferService transferService = OurCompanyTransferService.INSTANCE;
// 타행 송금서비스로 변경하고자 할 경우?
private AccountService() {}
public void transfer(Account withdrawalAccount, Account depositAccount) {
// business logic start ...
}
}
위와 같이 자원을 직접 명시하는 경우에는 송금 서비스의 구현체가 달라지는 요구사항에 대해 소스의 수정없이 대응하기가 어렵다.
의존 객체 주입 패턴
스프링과 같은 프레임워크의 사용이 보편적인 요즘의 개발환경에서 위와 같은 이슈에 대한 답은 쉽게 DI (Dependency Injection) 라고 말할 수 있을 것이다.
public class AccountService {
private TransferService transferService;
public AccountService(TransferService transferService) {
this.transferService = transferService;
}
public void transfer(Account withdrawalAccount, Account depositAccount) {
// business logic start ...
}
}
위 코드는 인스턴스 생성시점에 객체가 의존할 자원을 넘겨주는 방식으로 설계되어 있다. 이는 의존 객체 주입의 한 형태로 AccountService 클래스의 인스턴스 생성 시점에 TransferService 의 구현체를 주입해주면(생성자의 인자로 넘겨 멤버 필드에 할당해주면) 된다.
의존 객체 주입은 생성자, 정적 팩토리, 빌더 모두에 똑같이 응용할 수 있다. 아래의 포스팅은 스프링의 DI에 대해 잘 설명해주고 있다.
팩토리 클래스를 활용한 의존관계 주입.
생성자에 자원의 구현체를 직접 넘겨주지 않고, 팩토리 클래스를 넘겨주는 방법도 있다. 팩토리란 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 클래스를 의미한다. 이러한 역할에 따른 기능형태를 '팩토리 메서드 패턴' 이라고 한다. 자바 8의 Supply<T> 인터페이스는 팩토리를 표현하는 좋은 예시라고 할 수 있다. 팩토리와 관련된 내용은 추후 별도의 포스팅으로 다루어 보겠다.
정리
클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스의 동작에 영향을 준다면 싱글턴이나 정적 팩토리 리 클래스로 설계하는 것은 바람직하지 못하다. 이 자원들을 클래스가 직접 만들게 해서도 안된다.
필요한 자원을 만들거나, 자원을 만들어주는 팩토리를 생성자에 넘겨주어 의존객체 주입 구조를 만들자. 이 방법은 설계하고자 하는 클래스의 유연성, 재사용성, 테스트 용이성을 획기적으로 개선해 줄 것이다.
마치며
DI 개념은 평소에 너무 당연하게 사용하고 있어서 내용을 이해하는 것이 어렵지 않았다. 문제는 DI의 활용목적이 어플리케이션의 유연성, 재사용성, 테스트 용이성을 높여준다는 것인데, 내가 설계한 어플리케이션의 구조가 그런 기술적 장점을 전혀 활용하고 있지 못하고 있다는 생각이 든다. 단순히 DI라는 기술을 사용한다 라는 포인트보다 DI의 장점을 잘 활용할 수 있도록 어플리케이션의 설계구조에 대해 충분한 고민을 해봐야겠다.
'프로그래밍 > Effective Java' 카테고리의 다른 글
아이템 7. 다 쓴 객체참조를 해제하라 (0) | 2024.07.07 |
---|---|
아이템 6. 불필요한 객체 생성을 피하라 (0) | 2022.02.24 |
아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.01.18 |
아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2022.01.16 |
아이템 2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2022.01.10 |
- Total
- Today
- Yesterday