본문 바로가기

ComputerScience/DesignPattern

[SOLID] OCP: The Open–Closed Principle


* 이 글은 
Agile Software Development, Principles, Patterns, and Practices - Robert Martin 책 내용을 번역 및 요약하여 작성하였습니다.

OCP: The Open–Closed Principle

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. 

프로그램의 단일 변경으로 인해 종속 모듈에 대한 연쇄적인 변경이 발생하는 경우, 해당 디자인은 경직(Rigidity)의 흔적, 즉 코드의 수정이 어려워지는 경향이 있습니다.

 

OCP(개방-폐쇄 원칙)는 프로그램이 추가 변경 사항이 발생하더라도, 연쇄적인 변경을 일으키지 않도록 하는 것을 제안합니다.

OCP가 잘 적용된다면 그런 종류의 추가 변경 사항은 이미 작동하는 기존 코드를 수정하지 않고 새 코드를 추가할 수 있게됩니다.

Description

개방-폐쇄 원칙(OCP)을 준수하는 모듈은 주로 두 가지 주요 특성을 갖습니다.

  1. "확장 가능(Open for extension)": 이것은 모듈의 동작을 확장할 수 있다는 것을 의미합니다. 애플리케이션의 요구 사항이 변경될 때, 우리는 모듈을 새로운 동작으로 확장하여 그러한 변경 사항을 충족시킬 수 있습니다. 다시 말해, 모듈이 하는 일을 변경할 수 있습니다.
  2. "수정 불가능(Closed for modification)": 모듈의 동작을 확장해도 해당 모듈의 소스 코드나 바이너리 코드를 변경하지 않습니다. 모듈의 바이너리 실행 가능한 버전(링크 가능한 라이브러리, DLL 또는 Java .jar 형태)은 그대로 유지됩니다.

이 두 가지 특성은 서로 상반된 것처럼 보일 수 있습니다. 모듈의 동작을 확장하는 정상적인 방법은 해당 모듈의 소스 코드를 변경하는 것이기 때문입니다. 수정할 수 없는 모듈은 보통 고정된 동작을 갖는 것으로 간주됩니다.

모듈의 동작을 변경하지 않고 어떻게 모듈의 동작을 수정할 수 있는지, 모듈을 수정하지 않고 어떻게 모듈이 하는 일을 변경할 수 있는지는 OCP의 핵심 아이디어이며, 이를 실현하기 위해 다양한 디자인 패턴과 구조적 접근법이 사용됩니다.

Abstraction Is the Key

모듈은 추상화가 고정되어 있기 때문에 수정에 대해 닫혀 있을 수 있습니다. 그러나 모듈의 동작은 추상화의 새로운 파생 클래스를 만들어 확장할 수 있습니다.

위에서 Client 클래스는 Server 클래스를 사용합니다. 이 경우 Client가 다른 Server를 사용하고 싶다면 Client 클래스는 새 Server class를 사용하도록 변경되어야 합니다.

위에서 ClientInterface 클래스는 추상 멤버 함수를 가진 추상 클래스입니다. Client 클래스는 이 추상화를 사용하지만, Client 클래스의 객체는 일반적으로 Server 클래스의 객체를 사용합니다. Client 객체가 다른 서버 클래스를 사용하도록 하려면 ClientInterface 클래스의 새 파생 클래스를 만들 수 있습니다. Client 클래스는 변경하지 않고 그대로 유지될 수 있습니다.

Violating the OCP


위 DrawAllShapes 함수는 OCP를 준수하지 않습니다. 새로운 종류의 도형을 그릴 수 있도록 이 함수를 확장하려면 함수를 수정해야 합니다.
예를 들어, 삼각형을 그릴 수 있게 추가하려면 함수에서 case Triangle 같은 코드를 추가하는 것과 같은 수정을 해야 합니다. 새로운 종류의 도형을 그릴 때마다 함수를 수정해야 합니다.


이는 OCP에 어긋나는 예시로, 새로운 도형을 추가하려면 기존 함수를 수정해야 하므로 함수가 개방 폐쇄 원칙(OCP)을 준수하지 않는다는 것을 나타내고 있습니다. OCP를 준수하려면 새로운 도형을 추가하거나 기능을 확장하는 데 있어 함수의 수정 없이 확장 가능해야 합니다.

Conforming to the OCP

위 코드에서는 DrawAllShapes 함수를 변경할 필요가 없습니다.

따라서 함수의 동작은 수정하지 않고 기능은 확장할 수 있습니다.

실제로 Triangle 클래스를 추가하더라도 여기에 표시된 모듈 중 어떤 부분도 변경되지 않습니다.

물론 Triangle 클래스를 처리하기 위해 시스템의 일부가 변경되어야 할 것입니다. 그러나 여기에 표시된 모든 코드는 변경되지 않습니다.

 

실제 응용 프로그램에서는 Shape 클래스에 더 많은 메서드가 있을 것입니다. 그런데도 새로운 모양을 응용 프로그램에 추가하는 것은 여전히 매우 간단합니다. 필요한 것은 새로운 파생 클래스를 만들고 모든 함수를 구현하는 것뿐입니다. 변경이 필요한 위치를 찾아야 할 필요가 없습니다. 기존의 소스 모듈을 수정할 필요가 없으며 하나의 예외를 제외하고 기존의 이진 모듈을 다시 빌드할 필요가 없습니다.

실제로 Shape의 새로운 파생 클래스의 인스턴스를 생성하는 모듈을 수정해야 합니다. 일반적으로 이 작업은 main에서 또는 main에서 호출하는 함수에서 수행됩니다.

Anticipation and “Natural” Structure

위에서 수정된 코드에서 만약 도형을 그려야하는 순서를 정해야 한다는 변경 사항이 생겼다고 가정해 봅시다. 코드는 원을 찾고, 그 후에 사각형을 찾는 코드를 추가해야 할 것이며 이는 코드의 수정이 이루어지며 결과적으로 개방 폐쇄 원칙(OCP)을 위반합니다.

 

즉 도형 유형보다 순서가 더 중요한 시스템에서 이러한 코드 수정의 추상화가 자연스럽지 않다는 것입니다.

 

이것은 우리를 고민케 하는 결론으로 이어집니다. 일반적으로 모듈이 얼마나 "닫혀 있는" 것이든 항상 어떤 변경에 대해서는 닫히지 않을 것입니다. 모든 상황에서 자연스러운 모델이 없습니다.

닫힘이 완벽할 수 없기 때문에 그것은 전략적이어야 합니다. 즉, 디자이너는 자신의 디자인을 닫을 변경의 종류를 선택해야 합니다. 그는 가장 가능성 있는 변경의 종류를 추측하고 그러한 변경으로부터 자신을 보호할 수 있는 추상화를 만들어야 합니다. 

개발자는 애플리케이션이 시간이 지남에 따라 겪게 될 가능성 있는 변경의 종류에 대한 추측을 해야합니다. 이것은 경험에서 나온 통찰력을 필요로 합니다. 그리고 


또한, OCP를 준수하는 것은 비용이 많이 듭니다. 적절한 추상화를 생성하는 데 개발 시간과 노력이 필요합니다. 또한, 이러한 추상화는 소프트웨어 디자인의 복잡성을 증가시킵니다. 개발자들이 감당할 수 있는 추상화의 한도가 있습니다. 

Conclusion

많은 면에서 개방 폐쇄 원칙(OCP)은 객체 지향 디자인의 핵심입니다. 이 원칙을 준수함으로써 객체 지향 기술에서 주장되는 가장 큰 이점(즉, 유연성, 재사용성 및 유지 관리성)을 얻을 수 있습니다. 그러나 이 원칙을 단순히 객체 지향 프로그래밍 언어를 사용함으로써 달성되는 것은 아닙니다. 또한 응용 프로그램의 모든 부분에 남용된 추상화를 적용하는 것은 좋은 생각이 아닙니다. 오히려 개발자들이 프로그램의 일부분에서만 추상화를 적용하기로 결심해야 합니다. 사전에 추상화를 거부하는 것은 추상화 자체만큼 중요합니다.