책책책/토비의 스프링 3.1

[Spring] 객체지향 - 관심사의 분리

Deveun 2021. 6. 5. 18:48

토비의 스프링 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 Vol. 1 스프링의 이해와 원리

스프링의 핵심 프로그래밍 모델의 원리와 이에 적용된 다양한 디자인 패턴, 프로그래밍 기법의 이해를 돕는 책

www.acornpub.co.kr