지금까지 알아본 다양한 의존성 관리 기법들을 원칙이라는 관점에서 알아본다.

개방-폐쇄 원칙 (Open-Closed Principle)

소프트웨어 개체는 확장에 대해 열려있어야 하고, 수정에 대해서는 닫혀 있어야 한다.

OOP에 대해 설명할 때 나오는 SOLID 원칙 중 하나다. 그래서 어떻게 해야 개방-폐쇄 원칙을 지킬 수 있을까?

컴파일타임 의존성을 고정하고 런타임 의존성을 변경하라

개방-폐쇄 원칙은 런타임 의존성과 컴파일타임 의존성에 대한 이야기다. 유연하고 재사용 가능한 설계에서는 런타임 의존성과 컴파일타임 의존성은 서로 다른 구조를 가진다.

영화 예매 시스템에서 컴파일시점에는 Movie 클래스가 추상 클래스인 DiscountPolicy에 의존한다. 하지만 런타임 시점에는 구체 클래스인 AmountDiscountPolicy, PercentDiscountPolicy에 의존한다. 이렇게 런타임 의존성과 컴파일타임 의존성은 서로 다른 구조를 가진다.

또한 할인이 없는 경우나 중복 할인은 구현하는 경우에도 기존의 코드는 수정하지 않고(수정에는 닫혀있고) 새로운 추상 클래스의 구체 클래스를 추가하여 확장할 수 있다.(변경에는 열려있다.)

이처럼 개방-폐쇄 원칙을 수용하는 코드는 컴파일타임 의존성을 수정하지 않고 런타임 의존성을 쉽게 변경할 수 있는 구조라고 할 수 있다.

추상화가 핵심

개방-폐쇄 원칙의 핵심은 추상화의존하는 것이다. 추상화에서 생략되지 않고 남겨지는 부분은 다양한 상황에서의 공통점은 반영한 추상화의 결과물이다. 이런 공통 부분은 문맥이 변하더라도 바뀌지 않아야 한다. 따라서 추상화된 부분은 수정에 닫혀있다. 생략된 부분은 확장의 여지를 남긴다. 이것이 추상화가 개방-폐쇄 원칙을 가능하게 만드는 이유다.

하지만 단순히 어떤 개념을 추상화했다고 수정에 대해 닫힌 설계를 만들 수 있는 것은 아니다. 개방-폐쇄 원칙에서 폐쇄를 가능케하는 것은 의존성의 방향이다. 수정에 대한 파급효과를 최소화하기 위해서는 모든 의존성이 추상화를 바라봐야 한다.

2. 생성 사용 분리

객체 내에서 의존하는 추상클래스 A가 있을 때 이 A에 대한 구체 클래스의 인스턴스를 직접 생성해서는 안된다. 다른 구체 클래스가 필요할 때 이를 변경하려면 코드를 직접 수정해야하기 때문에 개방-폐쇄 원칙을 위반하게 된다.

물론 객체 생성을 피할수는 없다. 다만 책임이 문제다. 객체를 생성하는 책임과 사용하는 책임은 엄연히 분리되어야 한다. 다시말해 생성과 사용을 분리해야 한다.

일반적으로는 객체를 사용하는 클라이언트에게 다른 객체의 생성을 위임하고 이를 전달해준다. 영화 예매 시스템에서는 Client 가 DiscountPolicy의 구체 클래스를 직접 생성해 Movie에게 전달해주는 방식이다. 이제 Movie는 할인 정책에 대한 인스턴스를 사용하는 책임만 가지고 있다.