-
DDD - Dependency Injection과 AOP 4부 5부JAVA/DDD 2021. 1. 22. 22:17
우선 ProductRepository를 리팩토링하자.
- 구체적인 클래스에서 인터페이스를 추출하는 EXTRACT INTERFACE를 적용하자.
- 인터페이스 명은 ProductRepository로 하고 구현 클래스 명은 CollectionProductRepository로 하자.
12345678910ProductRepository.javapublic interface ProductRepository {public void save(Product product);public Product find(String productName);}cs 1234567891011121314151617CollectionProductRepository.javapublic class CollectionProductRepository implements ProductRepository {public ProductRepository() {}public void save(Product product) {Registrar.add(Product.class, product);}public Product find(String productName) {return (Product)Registrar.get(Product.class, productName);}}cs - EXTRACT INTERFACE 리팩토링을 통해 구체적인 클래스인 CollectionProductRepository가 인터페이스인 ProductRepository에 의존하도록 수정했다.
- 이제 OrderLineItem이 ProductRepository 인터페이스에 의존하도록 코드를 수정하자.
1234567891011121314OrderLineItem.javapublic class OrderLineItem {private ProductRepository productRepository = new CollectionProductRepository();public OrderLineItem() {}public OrderLineItem(String productName, int quantity) {this.product = productRepository.find(productName);this.quantity = quantity;}}cs - OrderLineItem의 productRepository 속성의 타입을 ProductRepository 인터페이스로 변경함으로써 OrderLineItem이 인터페이스에 의존하도록 수정했다.
- OrderLineItem의 생성자 내부에서는 ProductRepository 인터페이스 타입의 productRepository만을 사용하기 때문에 인터페이스에만 의존하고 구체적인 클래스에는 의존하고 있지 않다.
- 그러나 여전히 OrderLineItem 자체는 구체적인 클래스인 CollectionProductRepository와 강하게 결합되어 있다. 원인이 무엇일까?
사용(Use)으로부터 구성(Configuration)을 분리하라
- UML 다이어그램을 통해 현재 설계에 어떤 문제점이 있는지 살펴 보자.
- OrderLineItem이 직접 CollectionProductRepository를 생성하기 때문에 여전히 OrderLineItem과 CollectionProductRepository 간에 강한 결합 관계가 존재한다.
- 만약 CollectionProductRepository를 데이터베이스에 접근하도록 수정한다면 여전히 리팩토링을 수행하기 이전의 설계가 안고 있던 OCP 위반, 단위 테스트의 번거로움, 데이터베이스에 대한 종속성과 같은 문제점을 고스란히 안게 될 것이다.
- 문제의 원인은 객체의 구성(Configuration)과 사용(Use)이 OrderLineItem 한 곳에 공존하고 있다는 것이다.
- 현재 설계에서는 OrderLineItem이 직접 구체적인 클래스인 CollectionProductRepository와의 관계를 설정한다.
- 객체의 구성과 사용이 한 곳에 모여 있을 경우 객체 간의 결합도가 높아진다.
- 해결 방법은 외부의 객체가 OrderLineItem과 CollectionProductRepository 간의 관계를 설정하도록 함으로써 구성을 사용으로부터 분리시키는 것이다.
- 이처럼 협력하는 객체들의 외부에 존재하는 제 3의 객체가 협력하는 객체 간의 의존성을 연결하는 것을 의존성 주입(Dependency Injection)이라고한다.
- 직접 의존성 주입을 수행하는 인프라 스트럭처 코드를 작성할 수도 있으나 이를 수월하게 해주는 다양한 오픈소스 프레임워크가 개발되어 있다.
- 이 프레임워크들은 의존성을 주입할 객체들의 생명주기를 관리하는 컨테이너 역할을 수행하기 때문에 경량 컨테이너(lightweight container)라고도 불린다.
- 본 아티클에서는 이들 중 가장 폭 넓은 기능을 제공하면서도 가장 많은 개발자 커뮤니티의 지지를 받고 있는 경량 컨테이너인 Spring 프레임워크를 사용하자.
- 우선 OrderLineItem에서 CollectionProductRepository를 생성하는 부분을 제거한다.
- CollectionProductRepository와 OrderLineItem 간의 의존성을 삽입할 수 있도록 setter 메소드를 추가한다.
- 이처럼 setter 메소드를 이용하여 객체 간의 의존성을 삽입하는 것을 SETTER INJECTION이라고 한다.
- setter 메소드로 전달된 인자의 타입 역시 ProductRepository 인터페이스라는 것에 주의하자.
123456789101112131415161718OrderLineItem.javapublic class OrderLineItem {private ProductRepository productRepository;public OrderLineItem() {}public OrderLineItem(String productName, int quantity) {this.product = productRepository.find(productName);this.quantity = quantity;}public void setProductRepository(ProductRepository productRepository) {this.productRepository = productRepository;}}cs - Spring과 같은 경량 컨테이너를 사용함으로써 얻을 수 있는 또 하나의 장점은 불필요한 SINGLETON을 줄일 수 있다는 점이다.
- Spring은 컨테이너에서 관리할 객체를 등록할 때 객체의 인스턴스를 하나만 유지할 지 필요 시 매번 새로운 인스턴스를 생성할 지를 정의할 수 있다.
- 따라서 오버라이딩이 불가능하고 결합도가 높은 static메서드를사용하지 않고서도 객체를 SINGLETON으로 유지할 수 있다.
- 따라서 Spring을 사용하면 SINGLETON으로 구현된 Registrar를 인터페이스와 구체적인 클래스로 분리함으로써 낮은 결합도와 높은 유연성을 제공할 수 있다.
- EXTRACT INTERFACE 리팩토링을 적용하자.
1234567891011121314OrderLineItem.javapublic interface Registrar {void init();void add(Class<?> entryPointClass, EntryPoint newObject);EntryPoint get(Class<?> entryPointClass, String objectName);Collection<? extends EntryPoint> getAll(Class<?> entryPointClass);EntryPoint delete(Class<?> entryPointClass, String objectName);}cs - Registrar 인터페이스의 구현 클래스는 더 이상 SINGLETON일 필요가 없다.
- static 멤버 변수와 CREATION METHOD, static 메소드들을 인스턴스 메소드로 변경하자.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253public class EntryPointRegistrar implements Registrar {private Map<Class<?>,Map<String,EntryPoint>> entryPoints;public EntryPointRegistrar() {init();}public void init() {entryPoints = new HashMap<Class<?>, Map<String, EntryPoint>>();}public void add(Class<?> entryPointClass, EntryPoint newObject) {Map<String,EntryPoint> theEntryPoint =entryPoints.get(entryPointClass);if (theEntryPoint == null) {theEntryPoint = new HashMap<String,EntryPoint>();entryPoints.put(entryPointClass, theEntryPoint);}theEntryPoint.put(newObject.getIdentity(), newObject);}public EntryPoint get(Class<?> entryPointClass, String objectName) {Map<String,EntryPoint> theEntryPoint =entryPoints.get(entryPointClass);return theEntryPoint.get(objectName);}@SuppressWarnings("unchecked")public Collection<? extends EntryPoint> getAll(Class<?> entryPointClass) {Map<String,EntryPoint> foundEntryPoints =entryPoints.get(entryPointClass);return (Collection<? extends EntryPoint>)Collections.unmodifiableCollection(foundEntryPoints != null ?entryPoints.get(entryPointClass).values() : Collections.EMPTY_SET);}@SuppressWarnings("unused")public EntryPoint delete(Class<?> entryPointClass,String objectName) {Map<String,EntryPoint> theEntryPoint =entryPoints.get(entryPointClass);return theEntryPoint.remove(objectName);}}cs - 이제 CollectionProductRepository 클래스는 Registrar 인터페이스에 의존할 수 있다.
- SETTER INJECTION을 위해 setter 메소드를 추가하자.
12345678910111213CollectionProductRepository.javapublic class CollectionProductRepository implements ProductRepository {private Registrar registrar;public CollectionProductRepository() {}public void setRegistrar(Registrar registrar) {this.registrar = registrar;}}cs - Spring이 EntryPointRegistrar와 CollectionProductRepository 클래스의 생명 주기를 관리하도록 하기 위해서는 Spring 빈 컨텍스트에 두 클래스의 설정 정보를 정의해야 한다.
- EntryPointRegistrar를 id가 “registrar”인 빈으로 등록한 후 CollectionProductRepository의 registrar 프로퍼티에 의존성이 주입되도록 설정한다.
=
참조
'JAVA > DDD' 카테고리의 다른 글
DDD. ORM과 투명한 영속성 1부 (0) 2021.01.23 DDD - Dependency Injection과 AOP 6부 7부 (0) 2021.01.23 DDD - Dependency Injection과 AOP 3부 (0) 2021.01.22 DDD - Dependency Injection과 AOP 2부 (0) 2021.01.22 DDD - Dependency Injection과 AOP 1부 (0) 2021.01.11