변화를 겪으면서도 안정적이고, 첫 번째보다 오래 남는 설계를 만들려면 어떻게 해야할까..? 버트런드 마이어는 1988년에 개방 폐쇄 원칙(OCP)를 제안하면서 그 해결책을 제시했다.
OCP란 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀있어야한다는 원칙이다. 하나의 변경이 의존적인 모듈에서 단계적인 변경을 불러일으킬 때, 이 설계는 경직성의 악취를 낸다. OCP는 시스템을 리팩토링하여 나중에 일어날 그와 같은 종류의 변경이 더 이상의 수정을 유발하지 않도록 하려는 원칙이다. OCP가 잘 적용된 프로젝트는 원래 코드를 변경하는 것이 아니라 새로운 코드를 덧붙임으로써 변경을 할 수 있게 된다.
OCP를 따르는 모듈은 아래와 같은 2가지 속성을 가진다.
모듈의 행위가 확장될 수 있음을 의미한다. 요구사항이 변경될 때, 변경에 맞게 새로운 행위를 추가해 모듈을 확장시키고 모듈이 하는 일을 변경할 수 있다.
어떤 모듈의 행위를 확장하는 것이 그 모듈의 소스 코드나 바이너리 코드의 변경을 초래하지 않는다.
객체 지향 프로그래밍 언어에서는 고정되기는 해도 제한되지 않은 가능한 행위의 묶음을 표현하는 추상화를 만드는 것이 가능하다. 모듈은 추상화를 조작할 수 있다. 그리고 모듈은 이런 고정된 추상화에 의존하기 때문에 수정에 대해서는 닫혀 있을 수 있다. 모듈의 행위는 추상화의 새 파생 클래스(추상화한 인터페이스의 구현체, 추상 클래스를 상속받은 클래스 등)들을 만듦으로써 확장이 가능하다. 여기서 주의할 점은 추상 클래스는 자신을 구현하는 클래스보다도 클라이언트(해당 추상 클래스를 의존하는 클래스)에 더 밀접하게 관련되어 있다는 것이다. 그렇기에 해당 추상 클래스의 네이밍은 그 클래스를 구현하는 클래스를 기준으로 하는 것이 아니라 그 추상 클래스에 의존하는 클라이언트 클래스를 고려하여 네이밍하는 것이 맞다.(앞으로 이 점은 더 자세하게 살펴볼 것이라고 한다.)
OCP를 적용해서 모듈을 수정에 닫아놨다고 해도 닫혀 있지 않은 것에 대한 변경이 항상 존재한다는 것을 인지해야한다. 모든 상황에서 자연스러운 모델은 없다. 폐쇄는 완벽할 수 없기 때문에, 전략적이어야 한다. 그렇기 때문에 닫혀 있는 변경의 종류를 잘 선택해야한다. 가장 그럴 법한 종류의 변경을 추측하고, 그 변경에 대해 보호할 수 있는 추상화를 작성 해야 한다. 경험에서 얻은 통찰력을 이용하여 일어날 가능성이 가장 높은 변경에 대해서는 OCP를 적용하는 것이다.
하지만 이것은 쉽지 않다. 그렇다고 무작정 OCP를 적용하기에는 OCP는 비용이 많이 든다. 적절한 추상화를 만들기 위해서는 개발 시간과 노력이 들고, 이런 추상화는 설계의 복잡성을 높이기도 한다. 하지만 우린 어떤 변경이 있을 것이라고 미리 알아서 그에 맞춰서 OCP를 적용할 수 없다. 그러면 어떻게 해야할까?
답은 변경이 일어날 때까지 기다리는 것이다. 여러 변경을 예상하고 그것에 대비한 올가미를 놓는 방식은 좋지 않은 해결책일 수 있다. 그 올가미가 사용되지 않음에도 유지보수되어야 하기에 불필요한 복잡성의 악취만 진동을 할 것이다. 그렇기에 추상화가 실제로 필요할 때까지 기다렸다가 올가미를 놓는 것이다. 즉 우리는 첫 번째 총알은 그냥 맞고, 그 총에서 쏘는 다른 총알에 대해서는 확실히 보호하는 것이다.
이렇게 첫 총알을 먼저 맞기로 결정했다면, 총알이 빨리 그리고 자주 날아오는 것이 유리하다. 개발 과정에서 너무 멀어지기 전에 어떤 종류의 변경이 일어날 것인지 아는 것이 더 좋기 때문이다. 이런 어떤 종류의 변경이 일어나는지 기다리는 시간이 길어질수록, 적절한 추상화를 만드는 일은 어려워진다. 그렇기에 변경을 시뮬레이션할 필요가 있다. 그 방법은 아래와 같이 5가지가 있다.
- 테스트를 먼저 작성한다.
- 아주 짧은 주기(주보다는 일 단위로)로 개발한다.
- 기반구조보다 기능 요소를 먼저 개발하고, 자주 이 기능 요소를 이해당사자에게 보여준다.
- 가장 중요한 기능 요소를 먼저 개발한다.
- 소프트웨어를 빨리, 그리고 자주 릴리즈한다. 가능한 한 빠르게, 가능한 한 자주 고객과 사용자 앞에서 그것을 시연하는 것이다.
많은 면에서 OCP는 객체 지향 설계의 심장이라 할 수 있다. 이 원칙을 따르면 유연성, 재상용성, 유지보수성 등 많은 효용을 얻을 수 있다. 하지만 애플리케이션의 모든 부분에 마구 추상화를 적용하는 것도 좋은 생각은 아니다. 자주 변경되는 부분에만 추상화를 적용하기 위해 첫 번째 총알 정도는 맞을 생각을 하는 것이 좋다. 어설프고 과도한 준비에 기반한 추상화를 피하는 일은 추상화 자체만큼이나 중요하다.