9. 의존 처리 1: 직접 의존 객체 구함 코드에서 직접 의존 클래스의 객체 생성 public class WriteArticleService { public WriteResult write(Article article) { BoardService boardService = new DefaultBoardService(); Board board = boardService.getBoard(article.getBoardId()); if (board == null) { throw new BoardNotFoundException(article.getBoardId()); } IdGenerator idGenerator = new MemoryIdGenerator(); Integer nextId = idGenerator.nextId(); article.setId(nextId); ArticleDao articleDao = new MySQLArticleDao(); articleDao.insert(article); return WriteResult(aritcle); } } 의존 클래스를 바꾸면 코드 수정 필요 의존 클래스가 구현되기 전이면, 실행 불가
10. 의존 처리 2: 중앙에서 가져옴 객체 Locator (또는 디렉터리 서버)로부터 의존 객체 구함 public class WriteArticleService { public WriteResult write(Article article) { BoardService boardService = Locator.getBoardService(); Board board = boardService.getBoard(article.getBoardId()); if (board == null) { throw new BoardNotFoundException(article.getBoardId()); } IdGenerator idGenerator = Locator.getIdGenerator(); Integer nextId = idGenerator.nextId(); article.setId(nextId); ArticleDao articleDao = Locator.getArticleDao(); articleDao.insert(article); return WriteResult(aritcle); } } 로직과 상관없는 클래스에 대한 의존 발생
11. 의존 처리 3: Dependency Injection 의존 객체를 외부로부터 전달받음 public class WriteArticleService { private BoardService boardService; private IdGenerator idGenerator; private ArticleDao articleDao; public void setBoardService(BoardService boardService) { this.boardService = boardService; } … public WriteResult write(Article article) { Board board = boardService.getBoard(article.getBoardId()); if (board == null) { throw new BoardNotFoundException(article.getBoardId()); } Integer nextId = idGenerator.nextId(); article.setId(nextId); articleDao.insert(article); return WriteResult(aritcle); } }
12. 조립기 DI는 조립기를 통해 구현 public class Assembler { public void init() { DefaultBoardServiceboardService = new DefaultBoardService(); MemoryIdGenerator idGenerator = new MemoryIdGenerator(); MySQLArticleDao articleDao = new MySQLArticleDao(); WriteArticleService articleService = new WriteArticleService(); articleService.setBoardService(boardService); articleService.setIdGenerator(idGenerator); articleService.setArticleDao(articleDao); … } public WriteArticleService getWriteArticleService() { return articleService; } } Assembler assembler = new Assembler(); assembler.init(); WriteArticleService service = assember.getWriteArticleService(); service.write(…);
13. DI 기법 두 가지 생성자를 통한 의존 객체 전달받음 Setter 메서드를 통해 의존 객체 전달받음 기타: Method Injection public class WriteArticleService { private ArticleDao articleDao; public WriteArticleService(ArticleDao articleDao) { this.articleDao = articleDao; } } public class WriteArticleService { private ArticleDao articleDao; public setArticleDao(ArticleDao articleDao) { this.articleDao = articleDao; } }
14. 조립기의 구현 조립기는 객체를 담는 컨테이너 형태로 구현 됨 주요 컨테이너: Spring, Google Guice, … 컨테이너 어플리케이션
15. Spring DI 설정 예 XML 이용 객체간 의존 설정 <bean id="agent" class="com.wemade.am.agent.Agent" p:functionConfigLoader-ref="functionConfigLoader" p:functionConfigRepository-ref="functionTestLineRepository" p:scheduler-ref="scheduler" /> <bean id="functionConfigLoader" class="com.wemade.am.agent.loader.HttpClientFunctionConfigLoader" p:configLoadingUrl="${console.configLoadingUrl}" p:testConfigUnmarshaller-ref="testConfigUnmarshaller" /> <bean id="testConfigUnmarshaller" class="com.wemade.am.agent.loader.StaxTestConfigUnmarshaller" /> <bean id="functionTestLineRepository" class="com.wemade.am.agent.repo.FunctionTestLineRepositoryImpl" /> <bean id="scheduler" class="com.wemade.am.agent.scheduler.TimerScheduler" p:period="60000" p:testProcessor-ref="testProcessor" />
16. DI 장점 의존 객체의 변경이 용이 단위 테스트가 쉬워짐 기타 장점 코드 가독성 향상 재사용성 향상
17. 장점1 : 의존 객체 변경 용이 설정 파일 변경 만으로 의존 객체 변경 <bean id="agent" class="com.wemade.am.agent.Agent" p:functionConfigLoader-ref="functionConfigLoader" p:functionConfigRepository-ref="functionTestLineRepository" p:scheduler-ref="scheduler" /> <bean id="functionConfigLoader" class="com.wemade.am.agent.loader.HttpClientFunctionConfigLoader” /> <bean id="agent" class="com.wemade.am.agent.Agent" p:functionConfigLoader-ref="functionConfigLoader" p:functionConfigRepository-ref="functionTestLineRepository" p:scheduler-ref="scheduler" /> <bean id="functionConfigLoader" class="com.wemade.am.agent.loader.SoapFunctionConfigLoader” />
18. 장점2 : 단위 테스트 용이 Mock 객체를 이용한 단위 테스트 가능 실제 구현 없이 (또는 사용하지 않고) 테스트 가능 의존 클래스를 코드에서 직접 사용할 경우, 전체가 완성되기 전까지 테스트 과정이 수월하지 않음 WriteArticleService service = new WriteArticleService(); ArticleDao articleDao = mock(ArticleDao.class); … service.setArticleDao(articleDao); WriteResult result = service.write(…); assertNotNull(result.getArticle()); Mockito, easyMock 등 이용해서 실 구현없이 가짜 객체 생성 class WriteArticleService { public WriteResult write(Article article) { ArticleDao articleDao = new MySQLArticleDao(); articleDao.insert(article); … } } 의존 클래스 구현 없이 테스트 어려움
19. DI의 장점을 살리려면,,, 인터페이스에 의존할 것! 자바 인터페이스 or 추상 클래스의 추상 메서드 최대한 역할별로 클래스를 분리할 것 좋은 기준: 클래스 LoC(Line of Code) < 100~200