개방 폐쇄 원칙이란
개방 폐쇄 원칙
이란 클래스나 모듈은 확장에는 열려있어 쉽게 확장이 가능해야하고 변경에는 닫혀있어 자신이 의존하고 있는 클래스나 모듈의 변화에도 자신은 변경되지 않는다는 원칙이다.
예시
이를 코드로 살펴보면 다음과 같다.
public class A{
private B b;
public A(B b){
this.b = b;
}
public void execute(){
b.execute();
// 핵심 기능
}
}
위의 클래스 A는 B에 의존하고 있지만 b가 변경된다고 하더라도 B인터페이스를 통해 execute를 실행하고 있기 때문에 A에는 변경이 일어나지 않는다.
public inteface B{
public void execute();
}
B 인터페이스를 상속하고 있는 구현체라면 A는 b에 영향을 받지 않고 자신의 핵심 기능을 수행할 수 있다.
즉 A의 부가기능인 b.execute() 작업을 B라는 인터페이스를 통해 느슨하게 결합되어 작업을 수행한다고 볼 수 있다.
그리고 인터페이스를 통한 결합은 B가 수행하는 부가 기능에 대해서 확장에 대해 열려있다고 볼 수 있다.
인터페이스 B만 구현하고 A 클래스의 생성자를 통해 B의 구현체를 넣어주기만 한다면 얼마든지 B를 통해 부가기능을 확장할 수 있다.
또한 클래스 A는 다음과 같이 생성자를 통해서 인터페이스 B의 구현체를 선택하는 일을 자신을 사용하는 사용자에게 떠넘겼다.
public A(B b){
this.b = b;
}
이는 클래스 단위의 설계에서 나타나는 의존을 런타임시에 선택하여 의존하도록 이동시켰다.
클래스 A의 코드 상에는 인터페이스 B의 어떤 구현체를 입력받아 사용하는지 알 수 없고 알필요도 없다.
그저 클래스 A는 인터페이스 B의 구현체를 받아 부가기능을 수행하고 자신의 핵심 기능을 수행하는 곳에만 관심이 있기 때문이다.
따라서 다음과 같이 사용자가 직접 의존하는 인터페이스 B를 구현하는 구현체를 주입해주면 된다.
public class TestB implements B{
public TestB() {}
public void execute(){
// 부가 기능
}
}
public void main(String[] args){
B testB = new TestB();
// 런타임시에 자신이 의존하는 인터페이스 B의 구현체를 주입받는다.
A a = new A(testB);
}
결론
이를 통해서 개방 폐쇄 원칙을 달성할 수 있다.
- 클래스 A는 부가 기능인 B에 대해서 어떤 관심도 필요없고 영향도 받지않기 때문에 자신의 관심사인 핵심 기능만 수행하면 된다. 즉, B의 변경이 A의 핵심 기능에는 영향을 끼치지 않는다.
- 클래스 A의 부가 기능을 수행하는 B에 대해 얼마든지 확장이 가능하다. 인터페이스 B를 구현하기만 한다면 B가 수행하는 부가 기능을 얼마든지 확장할 수 있기 때문이다.
따라서 클래스 A는 부가 기능인 B에 대한 확장에 열려 있고 B의 변경에 대해서는 닫혀 있다.