깨끗한 코드를 구현하면 낮은 추상화 수준에서 관심사를 분리하기 쉬워진다.
높은 추상화 수준, 즉 시스템 수준에서도 깨끗함을 유지하는 방법을 살펴본다.
SW 시스템은 애플리케이션 객체를 제작하고 의존성을 서로 '연결'하는 준비 과정과
준비 과정 이후에 이어지는 런타임 로직을 분리해야 한다.
애플리케이션을 서로 겹치지 않는 개별 단위로 나누는 과정
→ 한 영역이 다른 영역과 격리되기 때문에 다른 영역에 영향을 주지 않고도 변경할 수 있으며, 애플리케이션의 전체적인 유지보수가 용이해진다.
가장 흔히 쓰이는 초기화 지연(Lazy Initialization)의 한계
public Service getService() {
if (service == null)
service = new MyServiceImpl(...);
return service;
}
장점
단점
getService
메서드가 MyServiceImpl
과 생성자 인수에 명시적으로 의존한다.MyServiceImpl
이 무거운 객체라면 단위 테스트 시 적절한 테스트 전용 객체를 할당해야 한다.많은 애플리케이션이 초기화 지연 기법을 수시로 사용한다. 그래서 전반적인 설정 방식이 곳곳에 흩어져 있다.
모듈성은 저조하며 대개 중복이 심각하다.
체계적이고 탄탄한 시스템을 만들고 싶다면 모듈성을 깨서는 절대로 안 된다.
객체를 생성하거나 의존성을 연결할 때도 마찬가지다.
설정 논리는 일반 실행 논리와 분리해야 모듈성이 높아진다.
시스템 생성과 시스템 사용을 분리하는 방법
객체가 생성되는 시점을 애플리케이션이 결정할 때 사용하는 방법
사용과 제작을 분리하는 강력한 메커니즘
필요한 객체를 직접 생성하는 것이 아닌, 외부로부터 필요한 객체를 받아서 사용 하는 것이다.
이를 통해 객체간의 결합도를 줄이고 코드의 재활용성을 높여준다.
의존성 주입은 제어 역전 기법을 의존성 관리에 적용한 메커니즘이다.
제어 역전에서는 한 객체가 맡은 보저 책임을 새로운 객체에게 전적으로 떠넘긴다.
새로운 객체는 넘겨받은 책임만 맡으므로 단일 책임 원칙을 지키게 된다.
setter
메소드나 생성자 인수를 통해 의존성을 주입한다.→ 초기화 지연으로 얻은 장점을 포기?
'처음부터 올바르게' 시스템을 만들 수 있다는 믿음은 미신이다. 대신에 우리는 오늘 주어진 사용자 스토리에 맞춰 시스템을 구현해야 한다.
테스트 주도 개발 (Test-driven Development), 리팩터링, 그에 따라 얻은 깨끗한 코드는 코드 수준에서 시스템을 조정하고 확장하기 쉽게 만든다.
로깅, 보안, 트랜잭션 등등 다수의 모듈에서 반복적으로 나타나는 부분
영속성과 같은 관심사는 애플리케이션의 자연스러운 객체 경계를 넘나드는 경향이 있다.
모든 객체가 전반적으로 동일한 방식을 이용하게 만들어야 한다.
횡단 관심사에 대처해 모듈성을 확보하는 방법론
Aspect: "특정 관심사를 지원하려면 시스템에서 특정 지점들이 동작하는 방식을 일관성 있게 바꿔야 한다."
자바에서 사용하는 Aspect와 유사한 메커니즘 세 개