OCP(Open Closed Principle)
OCP?
OCP는 개방 폐쇄 원칙(Open Closed Principle)의 약자로 확장에 대해선 개방적이고, 수정에 대해선 폐쇄적이어야함을 의미한다.
즉 기능 추가 요청이 오면 클래스를 확장을 통해 손쉽게 구현하면서, 확장에 따른 클래스 수정(기존 코드 변경)은 최소화 되어야함을 의미한다.
확장에 열려있다
- 모듈의 확장성을 보장하는 것을 의미한다.
- 새로운 변경 사항이 발생했을 때 유연하게 코드를 추가함으로써 애플리케이션의 기능을 큰 힘을 들이지 않고 확장할 수 있다.
변경에 닫혀있다
- 한번 완성된 클래스는 기능을 추가한다고 수정할 수 없다
- 객체를 직접적으로 수정하는건 제한해야 한다
- 즉 객체를 직접 수정하지 않고도 변경사항을 적용할 수 있도록 설계해야 한다.
OCP 추상화 설계
객체를 추상화함으로써 새로운 클래스를 추가할 때 기존 코드를 크게 수정할 필요 없이
적절하게 상속관계만 맞춰주어 유연하게 확장할 수 있다.
즉 기능 추가시 기존 코드를 변경시키는게 아닌 새로운 클래스를 만드는 형식을 띄는 것.
좋은 추상화 설계
-
변할 것과 변하지 않을 것을 엄격히 구분한다.
–> 변할 것은 하위 클래스에서, 변하지 않을 것은 상위 클래스에서 구현한다
- 두 모듈이 만나는 지점에 추상화 (인터페이스, 추상 클래스)를 정의한다
- 구현체에 의존하기 보다 정의한 추상화에 의존하도록 코드를 작성한다.
예제
package SOLID;
class AnimalType{
String type ;
AnimalType(String type){
this.type = type ;
}
}
class HelloAnimal{
void hello(AnimalType animal) {
if(animal.type.equals("Cat"))
System.out.println("냐옹");
else if(animal.type.equals("Dog"))
System.out.println("멍멍");
// 이 분기를 추가해줘야한다 --> OCP위반(한번 완성한 클래스는 기능 추가를 이유로 수정하면 안된다)
else if(animal.type.equals("Sheep"))
System.out.println("메에에");
}
}
public class OCP {
public static void main(String[] args) {
HelloAnimal hello = new HelloAnimal() ;
AnimalType cat = new AnimalType("Cat");
AnimalType dog = new AnimalType("Dog");
hello.hello(cat);
hello.hello(dog); // 문제 x
AnimalType sheep = new AnimalType("Sheep");
hello.hello(sheep); // 추가
}
}
문제
양의 울음소리, 사자의 울음소리 등 새로운 클래스에 대해 기능을 추가해주려면
기존 코드를 직접 수정해야 한다 (동물 추가될 때마다 코드 일일히 변경).
이는 결국 수정에는 닫혀있어야 한다는 원칙에 위배
해결
‘변할 것’에 해당하는 ‘울음소리’ 메서드는 각 하위클래스에서 구현(확장)하도록 해
기능 추가한다.
package SOLID;
abstract class AnimalType{
abstract void speak() ;
}
class CatType extends AnimalType{
@Override
void speak() {
System.out.println("냐옹");
}
}
class DogType extends AnimalType{
@Override
void speak() {
System.out.println("멍멍");
}
}
class HelloAnimal{
void hello(AnimalType animal) {
animal.speak();
}
}
public class OCP {
public static void main(String[] args) {
HelloAnimal hello = new HelloAnimal() ;
// AnimalType cat = new AnimalType("Cat");
AnimalType cat = new CatType();
AnimalType dog = new DogType();
hello.hello(cat);
hello.hello(dog);
AnimalType sheep = new SheepType() ;
hello.hello(sheep);
}
}
// 기능 추가 --> 이 클래스만 만들어주면 기존 클래스(HelloAnimal, AnimalType)에 영향 x
class SheepType extends AnimalType{
@Override
// int speak() { LSP 위반
void speak() {
// System.out.println("밥을 먹는다"); // LSP 위반
System.out.println("메에에");
}
}
대표: JDBC
OCP 언칙을 가장 잘 따르는 예시가 자바의 데이터베이스 인터페이스 JDBD이다.
만약 자바 어플리케이션에서 사용하고 있는 데이터베이스를 MySQL에서 Oracle로 바꾸고 싶다면,
기존 코드의 복잡한 하드코딩 변화 없이 그냥 Connection 객체 부분만 교체해준다
어떤 DBMS던 Connection이라는 추상화 객체에 담아서 해당 부모 클래스의 메서드로 동일하게 사용가능하기 때문이다
(LSP 원칙에 따라, 하위 클래스인 각각의 dbms에서 jdbc가 요구하는 기능을 수행하도록 구현되어있음)
결론
OCP에선 적당한 추상화 레벨을 선택함으로써, 어떤 행위에 대한 본질적인 정의를
서브 클래스에 전파하는 추상화 설계가 핵심이다.
이 때 이 ‘추상화할 특징’에 대해 명확히 정의하는 능력이 필요하다.
참고 사이트
💠 완벽하게 이해하는 OCP (개방 폐쇄 원칙) (tistory.com)
댓글남기기