유연한 설계에 대한 내용이다
자세한 내용이 궁금하면 오브젝트를 펼쳐보는 걸 추천드립니다.📖
개방-폐쇄 원칙(Open-Closed Principle, OCP)
로버트 마틴은 확장이 가능하고 변화에 유연하게 대응하는 설계 원칙 중 하나로 개방-폐쇄 원칙을 고안했다.
확장에는 열려있고, 수정에는 닫혀야 한다.
- 확장에 열려있다 : 새로운 요구사항이 생기면 변경에 맞게 새로운 '동작'을 애플리케이션에 추가할 수 있다.
- 수정에 닫혀있다 : 기존의 '코드'를 수정하지 않고 애플리케이션의 동작을 추가 및 변경할 수 있다.
개방-폐쇄 원칙은 런타임 의존성과 컴파일타임 의존성에 관한 이야기다.
핵심은 추상화다
개방-폐쇄 원칙의 핵심은 추상화에 의존하는 것이다.
추상화란 복잡성을 제거하고 핵심적인 부분만 남기는 기법이며, 개방-폐쇄 원칙의 관점에서 남겨지는 부분이 다양한 상황에서의 공통점을 반영한 추상화의 결과물이다. 공통부분이 문맥이 변해도 바뀌지 않는다. 수정할 필요가 없다는 것이고, 추상화를 통해 생략된 부분은 확장의 여지를 남긴다.
생성 사용 분리
유연하고 재사용 가능한 설계를 원하면 생성과 사용이라는 하나의 책임이 아닌 각각의 책임으로 따로 분리해야 한다.
시스템은 시작 단계와 실행 단계를 분리해야 한다.
가장 일반적인 방법은 객체를 생성하는 책임을 클라이언트에 옮기는 것이다. new를 사용하는 것은 특정 컨텍스트에 묶이는 결과라 했었다. 하지만 클라이언트는 상관이 없다는 전제가 깔려있다. 그런데 클라이언트도 특정 컨텍스트에 묶이지 않기 위해서 사용하는 방법이 있으니, FACTORY를 사용하는 것이다. FACTORY는 객체 생성에 특화된 객체로 외부에서 의존을 받는 것처럼 클라이언트는 FACTORY를 이용해서 의존을 받는다.
의존성 주입(Dependency Injection)
외부의 독립적인 객체가 인스턴스를 생성한 후 이를 전달해서 의존성을 해결하는 방법을 의존성 주입이라 부른다.
의존성 주입은 세 가지 방법이 있다.
- 생성자 주입(constructor injection) : 객체 생성 시점에 생성자를 통한 의존성 해결
- setter 주입(setter injection) : 객체 생성 후 setter를 통한 의존성 해결
- 메서드 주입(method injection) : 메서드 실행 시 인자를 통한 의존성 해결
* 서비스 로케이터(Service Locator) 패턴이라는 의존성 주입하는 방법도 있다는 것만 알아두자.
의존성 역전 원칙(Dependency Inversion Principle, DIP)
추상화와 의존성 역전
객체 사이의 협력이 존재할 때 그 협력의 본질을 담는 것은 상위 수준의 정책이다.
public class Movie {
private AmountDiscountPolicy discountPolicy;
}
Movie와 AmountDiscountPolicy 협력은 영화 가격을 계산하는 것이지 어떻게 할인 금액을 계산할 것인지는 본질이 아니다.
의존성은 Movie에서 AmountDiscountPolicy로 흐르는 게 아니라 AmountDiscountPolicy에서 Movie로 흘러야 한다.
해결 방법은 추상화로 모두 추상화에 의존하게 변경하면 된다.
public class Movie {
private DiscountPolicy discountPolicy;
}
정리하자면
- 상위 수준의 모듈과 하위 수준 모듈, 모두 추상화에 의존해야 한다.
- 추상화는 구체적인 사항에 의존해서는 안된다. 구체적인 사항은 추상화에 의존해야 한다.
이를 의존성 역전 원칙이라고 부른다.
유연성에 대한 조언
유연한 설계라는 말의 이면에는 복잡한 설계라는 의미도 있다. 유연한 설계는 변경에 대비하는 것이다. 미래에 일어날 변경이란 불안감을 대비하기 위해 불필요하게 복잡한 설계를 하는 경우가 있는데, 변경은 예상이 아니라 현실이다. 아직 일어나지 않은 변경은 변경이 아니다.
유연한 설계는 복잡하다. 단순하고 명료하면 유연한 설계가 아니다. 절차지향적인 방식은 코드의 정적인 표현이 실행 시점의 동적인 구조를 의미하고, 객체지향 코드에서의 클래스 구조는 발생 가능한 모든 객체 구조를 담는 틀일 뿐이다. 이를 파악하려면 직접 코드를 파악해보는 방법밖에 없다.
따라서 유연함은 단순성과 명확성의 희생 위에서 자라난다. 유연한 설계를 단순하고 명확하게 만드는 유일한 방법은 사람들 간의 긴밀한 커뮤니케이션뿐이다.
협력과 책임이 중요하다
초보자의 잦은 실수는 객체의 역할과 책임이 자리를 잡기 전에 성급하게 객체 생성에 집중하는 것이다. 생성은 책임 할당의 마지막 단계로 미뤄야 한다.
객체가 무엇이 되고 싶은지를 알게 될 때까지 객체들을 어떻게 인스턴스화할 것인지에 대해 전혀 신경 쓰지 않았다는 것이다. 이때 가장 중요한 관심거리는 마치 객체가 이미 존재하는 것처럼 이들 간의 관계를 신경 쓰는 일이다. 필자는 때가 되면 이러한 관계에 맞게 객체를 생성할 수 있을 것이라고 추측했다.
'서적 > 오브젝트: 코드로 이해하는 객체지향 설계' 카테고리의 다른 글
오브젝트 11_ 합성과 유연한 설계 (0) | 2021.06.10 |
---|---|
오브젝트 10_ 상속과 코드 재사용 (0) | 2021.06.09 |
오브젝트 08_ 의존성 관리하기 (0) | 2021.06.09 |
오브젝트 07_ 객체 분해 (0) | 2021.06.09 |
오브젝트 06_ 메시지와 인터페이스 (0) | 2021.06.09 |