## 목차 ##
- 도시를 세운다면?
- 시스템 제작과 시스템 사용을 분리하라
- 확장
- 자바 프록시
- 순수 자바 AOP 프레임워크
- 테스트 주도 시스템 아키텍처 구축
- 의사 결정을 최적화하라
- 명백한 가치가 있을 때 표준을 현명하게 사용하라
## 도시를 세운다면? ##
도시는 한 사람의 힘으로 세우고 관리하기는 무리다.
- 수도 관리 팀, 교통 관리 팀, 전력 관리 팀 등 각 분야를 담당하는 팀이 있어 잘 돌아간다.
- 도시가 잘 돌아가는 이유는 적절한 추상화와 모듈화 때문이다.
- 전기를 쓰기위해 발전설비의 세세한 사항까지 모두 알 필요가 있는가? 사용하기위한 적절한 인터페이스가 존재하기 때문에 사용자는 편안하다.
- 소프트웨어팀도 도시처럼 구성한다.
이번 장에서는 함수, 클래스를 넘어 시스템 수준에서도 깨끗함을 유지하는 방법을 살펴본다.
## 시스템 제작과 시스템 사용을 분리하라 ##
제작은 사용과 다르다.
소프트웨어 시스템은 준비과정(프로그램 구동준비 - 프로그램 전반적으로 사용하는 객체생성, 초기설정 등)과 런타임 로직(프로그램 동작 중 실행되는 로직)을 분리해야한다.
- 관심사 분리
- 관심사 분리는 소프트웨어에서 가장 중요한 설계기법들 중 하나다.
public Service getService() {
//초기화 지연을 사용한 객체 생성 및 할당
if (service == null)
service = new MyServiceImpl(...); // 모든 상황에 적합한 기본값일까?
return service;
}
- 초기화 지연(lazy initialization)
- 객체가 필요한 시점에 생성하는 방법이다.
- 프로그램 초기화 시에 객체를 생성하지 않으므로 초기 로딩시간이 빨라진다.
- 어떠한 경우에도 null을 반환하지 않는다.
- 그러나 정말로 필요하지 않으면 초기화 지연은 하지마라!
- Main 분리
- 관심사 분리의 한 가지 방법
- 생성과 관련된 코드는 main이나 main이 호출하는 모듈로 옮긴다.
- 나머지 시스템에서는 필요한 모든 객체가 생성되고 의존성이 연결되었다고 가정한다.
- 팩토리
- 객체가 필요한 시점 애플리케이션이 객체 생성
- 그러나 애플리케이션은 객체 생성의 세부사항을 알지 못한다.
주문처리 시스템에서 애플리케이션(orderProcession)이 주문(LineItem)을 생성해 추가한다. 이 때 팩토리 패턴을 사용하면 객체(주문) 생성시점은 애플리케이션이 결정하면서도 객체 생성 코드는 애플리케이션이 모른다.
- 의존성 주입(DI - Dependency Injection)
- 클래스 내부에서 객체를 직접 생성하는것이 아니라 외부에서 객체를 생성해서 넣어주는 것
- 단지 넣어주기만 한다고해서 의존성 주입이라 하지 않는다.
- 의존성 역전(IoC - Inversion of Control)을 통해 의존성을 분리
- IoC를 사용하면 한 객체는 자신이 사용할 객체를 직접 생성하지 않고 권한을 다른 오브젝트(main 같은)에 넘긴다.
- 중요한 것은 객체를 넘겨받고 이를 참조하는 변수는 인터페이스 혹은 추상클래스를 통해야 특정 구현 클래스에 대한 의존성을 완벽히 제거할 수 있다.
- 이렇게 상위 계층(객체를 사용하는 계층)이 하위 계층(객체 계층)에 대한 의존성을 분리하면 결합도가 낮아져 재사용성과 테스트의 용이함이 보장된다.
## 확장 ##
군락은 마을로, 마을은 도시로 성장한다.
- 이처럼 소프트웨어도 성장한다.
마을이 커지며 6차선 도로 확장공사를 할때가 되면…
- 왜 처음부터 크게 만들지 않았을까?
- 처음부터 조그마한 마을이 커질거란 기대로 6차선 도로를 뚫을 순 없는 것이다.
소프트웨어도 '처음부터 올바르게' 나중에 수정할 필요가 전혀 없도록 시스템을 만들 수 있다는 믿음은 미신이다.
- 애자일 개발, TDD, 리팩토링을 통해 시스템은 코드 수준에서 확장된다.
- '확장이 필요 없도록'이 아닌 '확장이 쉽도록'하는 시스템을 만들어야한다.
- 관심사 분리를 통해 설계한 소프트웨어 아키텍처는 점진적으로 발전할 수 있다.
AOP(Aspect Oriented Programming)
- 관점 지향 프로그래밍
- 애플리케이션 전체에 걸쳐 반복적으로 쓰이는 부가적인 알고리즘, 혹은 코드를 모듈화하여 재사용
- 흩어져 있던 관심사(로직)이라하여 흩어진 관심사(cross-cutting concerns)라 한다.
- 흩어진 관심사를 모듈화하고 핵심 비지니스 로직에서 분리하여 재사용하는 것이 AOP의 취지이다.
## 자바 프록시 ##
프록시(Proxy) : 대변인
자바에서 제공하는 AOP 매커니즘
프록시 패턴에서는 객체가 아닌 객체를 직접 조작하는 대리 객체를 통해 객체와 상호작용한다.
- 객체 생성과 관련된 부가적인 처리(흐름제어)를 한다.
- 흐름제어만 할 뿐 결과값을 조작하거나 변경시키면 안된다.
- 비지니스 로직의 변경 없이도 기능을 추가하거나 수정할 수 있다.
//인터페이스
public interface CommandExecutor {
public void runCommand(String cmd) throws Exception ;
}
//프록시 클래스
public class CommandExecutorProxy implements CommandExcutor {
private boolean isAdmin;
private CommandExecutor executor;
//프록시 객체 생성 시 유저 id와 pw를 넘겨받아 부가적인 처리를 한다.
public CommandExecutorProxy(String user, String pwd) {
if ( "seotory".equals(user) && "pw".equels(pwd) ) {
isAdmin = true;
}
executor = new CommandExcutorImpl();
}
@Override
public void runCommand(String cmd) throws Exception {
if (isAdmin) {
executor.runCommand(cmd);
} else {
if (cmd.trim().startsWith("rm")) {
throw new Exception("rm is only admin");
} else {
executor.runCommand(cmd);
}
}
}
}
프록시 패턴을 사용하면 아래와 같은 것이 가능
- 가상 프록시 : 필요 시점까지 실제 객체를 생성하지 않고 통제하여 메모리 절약
- 원격 프록시 : 다른 머신에 있는 객체 사용
- 보호 프록시 : 객체에 대한 액세스 권한 부여
- 정교한 참조 : 객체 생성과 관련하여 추가적인 처리
코드 양이 많아지며 깨끗한 코드를 작성하기 어렵다.
## 순수 자바 AOP 프레임워크 ##
- 순수 자바 관점을 구현
- 엔터프라이즈 프레임워크나 로직에 의존하지 않는다.
- POJO(Plain Old Java Object) 사용
- 진정한 POJO란 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 말한다.
- 특정 기술에 종속되지 않는 POJO 객체를 사용하기때문에 특정 기술과 환경에 종속되지 않는다.
- 따라서 전체 시스템의 결합도가 낮아지고 재사용성이 높아져 유지보수가 쉬워지며 테스트가 용이하다.
## 테스트 주도 시스템 아키텍처 구축 ##
- POJO를 통해 아키텍처 관심사를 엔터프라이즈 관심사로부터 분리한다면 진정한 TDD가 가능하다.
- BDUF(Big Design Up Front)가 필요없다. 오히려 해롭다.
## 의사 결정을 최적화하라 ##
- 모듈을 나누고 관심사를 분리하면 효율적인 관리 및 결정이 가능하다.
- POJO 시스템은 관심사를 효율적으로 분리하고 집중할 수 있게 해주어 최적의 결정을 위한 복잡성이 줄어든다.
## 명백한 가치가 있을 때 표준을 현명하게 사용하라 ##
- EJB2(Enterprise Java Bean 2)는 단지 표준이라는 이유만으로 사람들이 사용했다.
- 심지어 가벼운 설계로 충분했을 프로젝트에도..
- 표준을 사용하면 재사용이 쉽고, 경력자를 구하기 쉬우며 캡슐화도 쉽다.
- 그러나 그만한 사용 가치가 있는지 잘 판단하고 사용해야한다.
'소프트웨어 공학 > 클린코드' 카테고리의 다른 글
(클린코드) Chapter13 - 동시성 (0) | 2020.05.16 |
---|---|
(클린코드) Chapter12 - 창발성 (0) | 2020.05.16 |
(클린코드) Chapter10 - 클래스 (0) | 2020.05.16 |
(클린코드) Chapter09 - 단위 테스트 (0) | 2020.05.16 |
(클린코드) Chapter08 - 경계 (0) | 2020.05.16 |