토비의 스프링 1장: 오브젝트와 의존관계
관심사의 분리
하나의 관심사가 여기저기 흩어져 있으면 중복되는 코드로 존재하면, 그 관심사에 변경이 일어날 때 엄청난 수정이 발생된다.
먼저, 이는 메소드 추출 리팩토리 기법(extract method)으로 중복된 코드를 하나의 메소드로 정의하여 분리할 수 있다.
아주 기본적인 관심사의 분리 작업이다. 이를 좀 더 유연하게 확장해보자.
예로, 한 기업이 어떠한 Solution을 A고객사와 B고객사에 판매하려고 한다.
단, 이는 기업의 독자적인 기술이 들어가있기 때문에 Binary File만 제공하고 소스는 공개하지 않는다.
이 때, 고객사가 Solution을 사용하기 위한 Setting을 다르게 설정하고 싶다면 어떻게 할 수 있을 까?
(Solution는 하나의 클래스이고, 내부에는 Setting오브젝트 생성을 위한 init() 함수가 정의되어 있다고 하자)
(1) 상속 or 오버라이드를 통한 관심사의 분리
: init()함수를 protected로 선언하여 오버라이딩하거나 / Solution을 추상클래스로 만들고, init() 함수를 추상메소드로 선언하자.
그러면 A사와 B사는 각각 Solution을 상속한 ASolution, BSolution 서브클래스를 만들어
init() 함수를 입맛대로 수정하여 사용할 수 있다. ==> 템플릿 메소드 패턴(template method pattern)
이 함수가 어떤 Setting 오브젝트를 직접 생성한다면 ==> 템플릿 팩토리 패턴(template factory pattern)
[단점]
Java에서는 다중 상속을 지원하지 않는다.
그리고 만약 Solution2가 있다면? 고객은 또 다시 상속을 통한 init()함수를 중복으로 구현해야한다.
상속관계는 여전히 두 관심사에 대해 긴밀한 결합이 존재하는 것이다.
(2) 클래스의 분리와 인터페이스 사용
: 1번 방법의 단점 해결을 위해 아예 init() 함수 구현부분을 IfSettingMaker이라는 새로운 인터페이스로 분리하자.
이제 A,B사는 IfSettingMaker이라는 인터페이스를 구현하면 된다.
그리고 Solution에서는 인터페이스를 통해 만들어진 ASettingMaker, BSettingMaker 클래스를 받으면 된다.
==> 개방 폐쇄원칙(Open-Closed Principle) : Setting 기능의 확장에는 Open / 변화가 Solution자체에는 영향을 주지 않음
자, 이 때 생성자내부에서 직접 IfSettingMaker sm= new ASettingMaker() or IfSettingMaker sm= new BSettingMaker() 을 해야한다면?
또 다시 고객이 Solution을 직접 수정해야하는 일이 발생하므로, 이미 만들어 놓은 오브젝트를 생성자의 파라미터로 받도록 하자.
인터페이스를 만족하는 ASettingMaker, BSettingMaker와 같이 여러 클래스가 있을 때 이 관계설정의 책임을 분리하는 것이다.
이렇게 하면 Solution 클래스 자체에서는 어떤 SettingMaker 오브젝트를 사용할지 결정하지 않아도 되고, 전략/목적에 따라 외부에서 적절한 IfSettingMaker 구현 오브젝트를 선택해서 사용할 수 있다.
==> 전략패턴
마침내!
클래스분리 -> 인터페이스 사용 -> 관계 책임 분리 과정을 통해 관심사의 분리를 이루어 냈다.
클라이언트인 제 3의 클래스 (SolutionTest)에서 A, B사는 자신들의 ASettingMaker, BSettingMaker 클래스를 만들어 이를 Solution에 주입시켜주기만 하면 된다!
SolutionTest.java // 클라이언트. Solution과 SettingMaker 구현 클래스와의 관계 설정을 담당.
public class SolutionTest {
public static void main(String[] args) {
IfSettingMaker aSettingMaker = new ASettingMaker();
Solution aSolution = new Solution(aSettingMaker);
}
}
Solution.java // binary 파일로 제공될 모듈. 관심사 분리를 통해 고객사는 이 파일을 수정할 필요가 없음.
public class Solution {
private IfSettingMaker settingMaker;
public Solution(IfSettingMaker settingMaker) {
this.settingMaker = settingMaker;
// 인터페이스만 만족한다면 어떻게 생성된 객체인지 관여하지 않음.
}
public void func1() {
Setting setting = settingMaker.init();
// Solution의 기능1 구현내용이 쓰임
// 특별한 알고리즘
}
public void func2() {
Setting setting = settingMaker.init();
// Solution의 기능2 구현내용이 쓰임
// 특별한 알고리즘2
}
}
IfSettingMaker.java // 독자적인 클래스를 생성할 수 있도록 제공해주는 인터페이스
public interface IfSettingMaker {
public Setting init();
}
ASettingMaker.java // 고객이 직접 생성하는 클래스
public class ASettingMaker implements IfSettingMaker{
public Setting init() {
// A고객사가 필요한 방식으로 Setting을 생성
// Customizing
return aSetting;
}
}
[참고서적] http://www.acornpub.co.kr/book/toby-spring3.1-vol1#spring3
'책책책 > 토비의 스프링 3.1' 카테고리의 다른 글
[Spring] 자바 테스팅 프레임워크 JUnit (개념편) (0) | 2021.07.16 |
---|---|
[Spring] 스프링 XML 설정 (0) | 2021.07.02 |
[Spring] 의존관계 주입 (DI: Dependency Injection) (0) | 2021.07.01 |
[Spring] 스프링 IoC컨테이너 ApplicationContext (0) | 2021.07.01 |
[Spring] 제어의 역전 (IoC: Inversion of Control) (0) | 2021.06.05 |