SOLID 원칙 중 두번째인
1. OCP (=Open-Closed Principle, 개방 폐쇄 원칙)
"확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다."
- 다른 말로 "기능변경하거나 확장할 수 있으면서 그 기능을 사용하는 코드는 수정하지 않는다."를 의미
- 적용방법 (2가지)
=> 변화하는 부분을 추상화함으로써 기존 코드를 수정하지 않고도, 확장을 할 수 있게 만들어 줍니다.
#1) 변화하는 부분을 추상화하는 것 -> 주로, 인터페이스를 통해서 구현을 합니다.
#2) 상속을 이용하는 것
- ex) 상속을 사용
ⓐ HTTP 응답 프로토콜에 맞춰 데이터를 전송해 주는 ResponseSender 클래스가 존재한다.
ⓑ 이때, 기존 기능에 압축 기능을 추가 필요한 상황 발생
ⓒ ResponseSender를 상속받은 ZippedResponseSender 클래스를 생성하여 Override를 통해 기존 기능에 압축 기능을 추가하면
(ResponseSender 클래스는 변경 없으므로 확장에는 열려 있으면서 변경에는 닫혀있으므로) OCP를 준수하게 된다.
- 개방 폐쇄 원칙을 지키지 않았을 때의 문제사례 2가지
ㄴ> 1) 다운 캐스팅을 하는 경우, 클래스에서 변경이 일어날 때마다 해당 메소드를 수정이 필요해진다.
>> instanceof와 같은 타입 확인 연산자가 사용된다면 해당 코드는 개방 폐쇄 원칙을 위반할 가능성이 높으므로 타입 캐스팅 후 실행되는 메소드가 변화 대상인지 확인해야 합니다. |
Ex) drawSpecific(Character character) 메소드에서 Character를 상속받은 Player, Enemy, Missile 클래스 중 Missile만 'if(character instanceof Missile)'로 체크하여 다르게 동작하도록 개발한 경우 => Character클래스에서 변경이 일어날 때마다 해당 메소드를 수정이 필요해진다. => 차라리 drawSpecific() 메소드가 다른 객체들에서도 적용할 만한 메소드라면, Character 타입에 추가하라는 것입니다. |
ㄴ> 2) 비슷한 if~else 블록이 계속 필요해진다.
Ex) Enemy 클래스에서 정해진 패턴에 따라 경로를 이동하는 코드가 필요하다면 |
public void draw() { if(pathPattern == 1) { x += 4; } else if(pathPattern == 2) { y += 10; } else if(pathPattern == 4) { x += 4; y += 10; } ...; // 그려 주는 코드 } |
=> Enemy 클래스에 새로운 경로 패턴을 추가할 때마다 draw() 메소드에는 새로운 if문이 생깁니다. 즉, 경로를 추가하는데 Enemy 클래스는 변경에 닫혀있지 않은 것입니다. => 따라서, (x, y)와 같은 경로 패턴을 추상화 해야합니다. |
- OCP원리를 적용하면 좋은 점
- 하나의 변화가 다른 곳에도 연쇄적으로 변화를 일으키는 것을 방지
- 유연성
[출처]
- 위키 : https://ko.wikipedia.org/wiki/%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84_%EC%9B%90%EC%B9%99
- SRP
-> https://steady-coding.tistory.com/370
- https://www.nextree.co.kr/p6960/
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - 최범균