"복잡성은 죽음이다. 개발자에게서 생기를 앗아가며, 제품을 계획하고 제작하고 테스트하기 어렵게 만든다."
-레이 오지, 마이크로소프트 CTO-
'제작'과 '사용'은 다르다.
-> 소프트웨어 시스템은 준비 과정(애플리케이션 객체를 제작하고 의존성을 연결하는)과 런타임 로직(준비 과정 이후에 이어지는) 런타임 로직을 분리해야 한다.
public Service getService() {
if (service == null)
service = new MyServiceImpl(...); // Good enough default for most cases?
return service;
}
위와 같은 코드를 '초기화 지연', '계산 지연' 기법이라 부른다.
장점도 존재한다.
하지만 이 메서드는 단점이 크다.
체계적이고 단단한 시스템을 만들고 싶다면 위와 같은 '손쉬운 기법'으로 모듈성을 깨서는 안된다. 또한 '설정 논리'와 '일반 실행 논리'를 분리해야 모듈성이 높아진다.
위의 흐름을 보면
때로는 객체가 생성되는 시점을 애플리케이션이 결정할 필요도 있다.
즉 위와 같이 애플리케이션은 생성되는 방법을 모른다.
의존성 관리의 관점에서 "객체는 그 자신의 의존성들을 직접 생성하지 말고 다른 전담 매커니즘에 넘겨야한다."
MyService myService = (MyService)(jndiContext.lookup("NameOfMyService"));
위 코드는 호출하는 쪽에서 lookup 메서드가 무엇을 리턴하는지 모르면서 의존성을 해결할 수 있다.
진정한 의존성 주입은 한 발 더 나아가서, 클래스가 의존성을 해결하려하지 않는다.
-> 의존성을 주입하는 방법으로 setter 메서드나 생성자 인수를 제공한다.
처음부터 올바르게 시스템을 만들 수 없다. 그저 오늘은 주어진 스토리대로 시스템을 구현하고 내일은 새로운 스토리에 맞춰 확장하고 조정해야 한다.
-> 애자일 방식의 핵심!
(TDD, 리팩터링은 코드 수준에서 시스템을 조정하고 확정하기 쉽게 만든다.)
/* Code 2-1(Listing 11-1): An EJB2 local interface for a Bank EJB */
package com.example.banking;
import java.util.Collections;
import javax.ejb.*;
public interface BankLocal extends java.ejb.EJBLocalObject {
String getStreetAddr1() throws EJBException;
String getStreetAddr2() throws EJBException;
String getCity() throws EJBException;
String getState() throws EJBException;
String getZipCode() throws EJBException;
void setStreetAddr1(String street1) throws EJBException;
void setStreetAddr2(String street2) throws EJBException;
void setCity(String city) throws EJBException;
void setState(String state) throws EJBException;
void setZipCode(String zip) throws EJBException;
Collection getAccounts() throws EJBException;
void setAccounts(Collection accounts) throws EJBException;
void addAccount(AccountDTO accountDTO) throws EJBException;
}
/* Code 2-2(Listing 11-2): The corresponding EJB2 Entity Bean Implementation */
package com.example.banking;
import java.util.Collections;
import javax.ejb.*;
public abstract class Bank implements javax.ejb.EntityBean {
// Business logic...
public abstract String getStreetAddr1();
public abstract String getStreetAddr2();
public abstract String getCity();
public abstract String getState();
public abstract String getZipCode();
public abstract void setStreetAddr1(String street1);
public abstract void setStreetAddr2(String street2);
public abstract void setCity(String city);
public abstract void setState(String state);
public abstract void setZipCode(String zip);
public abstract Collection getAccounts();
public abstract void setAccounts(Collection accounts);
public void addAccount(AccountDTO accountDTO) {
InitialContext context = new InitialContext();
AccountHomeLocal accountHome = context.lookup("AccountHomeLocal");
AccountLocal account = accountHome.create(accountDTO);
Collection accounts = getAccounts();
accounts.add(account);
}
// EJB container logic
public abstract void setId(Integer id);
public abstract Integer getId();
public Integer ejbCreate(Integer id) { ... }
public void ejbPostCreate(Integer id) { ... }
// The rest had to be implemented but were usually empty:
public void setEntityContext(EntityContext ctx) {}
public void unsetEntityContext() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbLoad() {}
public void ejbStore() {}
public void ejbRemove() {}
}
위 코드와 같은 EJB2 객체 구조는 문제점이 있다.
= 이론적으로는 독립된 형태로 구분할 수 있지만 실제로는 코드에 산재하기 쉬운 부분
(transaction, authorization, logging 등)
이 문제를 AOP(Aspect-Oriented-Programming)으로 해결하려한다.
아래는 aspect-like-mechanism 이다.