SOLID 원칙(2) - 개방 폐쇄 원칙 알아보기 OCP
# SOLID 원칙 - OCP (개방 폐쇄 원칙)
“소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.”
소프트웨어 개발 작업에 이용된 많은 모듈 중에 하나에 수정을 가할 때 그 모듈을 이용하는 다른 모듈을 줄줄이 고쳐야 한다면, 이와 같은 프로그램은 수정하기가 어렵다. 개방-폐쇄 원칙은 시스템의 구조를 올바르게 재조직(리팩토링)하여 나중에 이와 같은 유형의 변경이 더 이상의 수정을 유발하지 않도록 하는 것이다. 개방-폐쇄 원칙이 잘 적용되면, 기능을 추가하거나 변경해야 할 때 이미 제대로 동작하고 있던 원래 코드를 변경하지 않아도, 기존의 코드에 새로운 코드를 추가함으로써 기능의 추가나 변경이 가능하다.
1. 확장에 대해 열려 있다.
이것은 모듈의 동작을 확장할 수 있다는 것을 의미한다. 애플리케이션의 요구 사항이 변경될 때, 이 변경에 맞게 새로운 동작을 추가해 모듈을 확장할 수 있다. 즉, 모듈이 하는 일을 변경할 수 있다.
2. 수정에 대해 닫혀 있다
이것은 모듈의 동작을 확장할 수 있다는 것을 의미한다. 애플리케이션의 요구 사항이 변경될 때, 이 변경에 맞게 새로운 동작을 추가해 모듈을 확장할 수 있다. 즉, 모듈이 하는 일을 변경할 수 있다.
3. 추상화를 통한 개방-폐쇄 원칙
객체 지향 프로그래밍 언어(JAVA, C++ 등)에서는 고정되기는 해도 제한되지는 않은, 가능한 동작의 묶음을 표현하는 추상화가 가능하다. 모듈은 추상화를 조작할 수 있다. 이런 모듈은 고정된 추상화에 의존하기 때문에 수정에 대해 닫혀 있을 수 있고, 반대로 추상화의 새 파생 클래스를 만드는 것을 통해 확장도 가능하다. 따라서 추상화는 개방-폐쇄 원칙의 핵심 요소이다.
4. 개방-폐쇄 원칙과 객체 지향 언어의 관계
개방-폐쇄 원칙은 객체 지향 프로그래밍의 핵심 원칙이라고 할 수 있다. 개방-폐쇄 원칙을 따르지 않는다고 해서 객체 지향 언어(Java, C++ 등)을 구현이 불가능한 것은 아니지만 이 원칙을 무시하고 프로그래밍을 한다면, 객체 지향 프로그래밍의 가장 큰 장점인 유연성, 재사용성, 유지보수성 등을 결코 얻을 수 없다. 따라서 객체 지향 프로그래밍 언어에서 개방-폐쇄 원칙은 반드시 지켜야할 기본적인 원칙이다.
알아보기
소셜 로그인을 만들려고 한다.
public class OAuthHandler {
public void oauthConnect(OAuthGoogle oAuthGoogle) {
oAuthGoogle.connect();
}
}
OAuthHandler 는 OAuthGoogle 의 connect 메소드를 호출한다.
하지만, 추후 Facebook OAuth 연동이 추가되면 어떻게 될까?
public class OAuthHandler {
public void oauthConnect(String channel) {
if(channel.equals("google")) {
new OAuthGoogle().connect();
} else if(channel.equals("facebook")) {
new OAuthFacebook().connect();
}
}
}
여러 예시가 있겠지만 현재 상태로는 OAuthHandler 를 수정해야 될 수 밖에 없다.
그러므로, OAuth 기능에 채널을 추가 할려고 할 때마다 OAuthHandler 도 신경써야하는 일이 발생한다. 채널을 추가 하기에는 어렵고 수정은 해야 하는 상황이 되어버렸다.
확장은 열려있고 수정은 닫혀있어야 하는 개방/폐쇄 원칙을 위반하게 된다.
추상화를 통해 해당부분을 해결 해 보자.
OAuthHandler 가 직접적으로 OAuthGoogle 을 호출 하던 것을 OAuthClient 인터페이스를 두어 OAuthHandler 가 각각의 구현체(OAuthGoogle, OAuthFacebook) 을 모르게 하였다.
이제는 OAuthHandler 가 OAuthClient 에게 메시지를 보낼 뿐 OAuth 채널이 추가 되더라도 변동 상황이 없어졌다.
public interface OAuth {
void connect();
}
public class OAuthGoogle implements OAuth {
@Override
public void connect() {
}
}
public class OAuthFacebook implements OAuth {
@Override
public void connect() {
}
}
public class OAuthHandler {
public void updateExpireDate(OAuth oAuth) {
oAuth.connect();
}
}
이제는 OAuthNaver 가 추가 되더라도 OAuthNaver 만 신경쓰면된다.
OAuth 기능 확장은 자유로워 졌고, OAuthHandler 의 수정은 없어지게 되었다.
정리
OAuthHandler 는 변경에 닫혀 있고 OAuth 는 변경에 열려 있는것처럼,
변경 가능한것과 덜 변경되는 부분을 잘 고려해서 설계 한다면, OCP 장점처럼 유연성과 유지보수성을 증가 시킬 수 있을 것 같다.