파트1. 애자일 개발
적어둘 문구
- 테스트를 먼저 작성하는 것은 설계 의사결정의 차이를 식별하는 것이다. p36
- 코드보다 테스트를 먼저 작성하면 설계가 개선된다. p39
- 리팩토링이란 외부 행위를 바꾸지 않으면서 내부 구조 개선. p45
- 한 번만 호출되는 함수를 추출하는 경우에 성능에 부정적인 영향을 줄 수 있을 지 모르지만 대부분의 경우 향상된 가독성이 몇 나노초 이상의 가치가 있다. p57
파트2. 애자일 설계
적어둘 문구
- 설계는 명료한 형태로 유지되어야 한다.
- 소스 코드 역시 명료한 형태로 유지 되어야 한다. p122
챕터9. 개방 폐쇄 원칙(OCP)
적어둘 문구
폐쇄는 추상화에 기반을 둔다. 모듈이 얼마나 ‘닫혀’있던 간에 닫혀 있지 않은 것에 대한 변경은 항상 존재한다. 모든 상황에서 자연스러운 모델은 없다. 폐쇄는 완벽할 수 없기 때문에 전략적이어야 한다. 즉, 설계자는 자신의 설계에서 닫혀 있는 변경의 종류를 선택해야 한다.
- 가장 그럴 법한 종류의 변경을 추측하고, 그
변경에 대해 보호할 수 있는 추상화를 작성해야 한다.
이런 변경 점을 알 수 있는 방법
- 적절한 연구를 하고
- 적절한 질문을 던지고
- 경험과 상식을 이용한다.
올가미 놓기
- 소프트웨어를
불필요한 복잡성의 부하에서 구하려면 우리 자신이 한 번 당할 각오가 필요하다. - 처음에는 코드가 변경되지 않을 것이라 생각하고 작성한 다음에 변경이 일어나면 그 변경을 보호하는 추상화를 구현한다.
챕터 10. 리스코프 치환 원칙(LSP)
- 가장 바람직한 상속 계층 구조란 무엇일까
서브 타입은 그것의기반 타입으로 치환 가능해야 한다 .
IS-A(B is a A)
- IS-A는 행위에 관한 것이다.
- LSP는 ODD에서 IS-A 관계는 합리적으로 가정할 수 있고 클라이언트가 의존하는 행위와 관련이 있다.
- IS-A 관계가 아니라면 두개의 공통 메소드를 묶어서 하나의 부모로 만들어서 분리하는 방법도 있다.
- 공통 인자 추출로도 볼 수 있다.
- 어떤 클래스의 집합이 모두 같은 책임을 진다면, 공통 슈퍼클래스에서 그 책임을 상속받아야 한다.
DBC(Design by contract)
- 계약에 의한 설계
- 클라스의 작성자는 클래스의 계약사항을 명시적으로 정한다.
- 각 메소드의 사전조건과 사후조건을 선언하는 것으로 구체화 할 수 있다.
파생 클래스에서 루틴 재선언
원래 사전조건과 같거나 더 약한 수준에서 그것을 대체할 수 있고 원래 사후조건과 같거나 더 강한 수준에서 그것을 대체할 수 있다.
- 사전조건은 같거나 더 약한 수준
- 파생 클래스(자식)가 부모보다 더 까다로운 조건을 요구하면 안 된다는 뜻
- 부모가 0 이상이면 호출해도 됨이라고 했는데, 자식이 10 이상이어야 호출 가능으로 바꾸면 안 된다는 것
- 사후조건은 같거나 더 강한 수준
- 파생 클래스가 부모보다 더 약한 결과를 보장해선 안 된다는 뜻
- 부모가 결과는 항상 양수를 보장한다면, 자식은 항상 양수이고, 심지어 10 이상까지는 괜찮지만, 음수일 수도 있음으로 약하게 만드는 것
챕터 11. 의존 관계 역전 원칙(DIP)
- 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다.
- 둘 모두 추상화에 의존해야 한다.
주의
- 하위 수준의 모듈의 변경이 애플리케이션의 본질을 담고 있는 상위 수준의 모듈에 영향을 주면 안된다.
- 상위 수준의 모듈이 하위 수준의 모듈에 의존하게 된다는 것은 상위 수준의 모듈의 재사용성이 떨어진다는 의미이다.
정의
- 어떤 변수도 구체 클래스에 대한 포인터나 참조값을 가져선 안된다.
- 어떤 클래스도 구체 클래스에서 파생되어서는 안된다.
- 어떤 메소드도 그 기반 클래스에서 구현된 메소드를 오버라이드해서는 안된다.
내재하는 추상화
- 상위 수준의 정책이라는 것은 애플리케이션에 내재하는 추상화이자 구체적인 것이 변경되더라도 바뀌지 않는 것
- 예를 들어 Lamp라는 것이 있다면 휴대폰 버튼을 끄거나 켤수도 있고 콘솔의 LED를 끄거나 켤 수 있다는 의미로 나뉘어 질 수 있지만 끄고 켠다라는 개념은 변하지 않는다.
챕터 12. 인터페이스 분리 원칙(ISP)
- 주제
- 클라이언트가 자신이 사용하지 않는 메소드에 의존하도록 강제되어선 안된다.
- 비대한 인터페이스를 가지는 클래스는 응집력이 없는 인터페이스를 갖는 클래스이다.
- 클라이언트는 응집력이 있는 인터페이스를 갖는 추상 기반 클래스에 대해 알고 있어야 한다.
- 클라이언트의 분리는 인터페이스의 분리를 의미한다.
주의
- 클라이언트가 사용하지 않는 메소드에 의존할 때 코드 변경 시 의도치 않은 문제에 직면할 수 있다.
해결 방법
- 하나의 인터페이스를 아래와 같이 분리하다.
public interface OrderService {
void createOrder();
void cancelOrder();
void sendOrderNotification();
void generateOrderReport();
}
ISP + 위임 적용: 인터페이스 분리 + 위임
=>
public interface OrderCreator {
void createOrder();
void cancelOrder();
}
public interface OrderNotifier {
void sendOrderNotification();
}
public interface OrderReporter {
void generateOrderReport();
}- 위임을 통한 분리
public class OrderFacade {
private final OrderCreator creator;
private final OrderNotifier notifier;
private final OrderReporter reporter;
public OrderFacade(OrderCreator creator,
OrderNotifier notifier,
OrderReporter reporter) {
this.creator = creator;
this.notifier = notifier;
this.reporter = reporter;
}
public void createOrder() {
creator.createOrder(); // 위임
notifier.sendOrderNotification();
}
public void cancelOrder() {
creator.cancelOrder(); // 위임
}
public void generateReport() {
reporter.generateOrderReport(); // 위임
}
}- 다중 상속을 통한 분리
public class OrderService
implements OrderCreator, OrderNotifier, OrderReporter {
@Override
public void createOrder() {
// 주문 생성 로직
}
@Override
public void cancelOrder() {
// 주문 취소 로직
}
@Override
public void sendOrderNotification() {
// 알림 발송 로직
}
@Override
public void generateOrderReport() {
// 리포트 생성 로직
}
}