본문 바로가기
개발용어

SOLID 원칙 - 개방폐쇄원칙, OCP (Open Closed Principle)

by devscb 2022. 5. 7.
반응형

SOLID 원칙 - 개방폐쇄원칙, OCP (Open Closed Principle)


개방폐쇄원칙 원칙이란? (개방폐쇄원칙, OCP ,Open Closed Principle)


개방형 폐쇄 원칙은 객체 지향 설계의 5가지 SOLID 원칙 중 하나이다.
객체 지향 프로그래밍에서 개방폐쇄의 원리는 "소프트웨어 엔티티(클래스, 모듈, 기능 등)는 확장을 위해 개방되어야 하지만 수정에 대해서는 폐쇄되어야 한다" 라는 원칙입니다.
이 원칙을 지킨다면, 소스 코드를 수정하지 않고 동작을 확장하도록 허용할 수 있습니다.
또한, 기능 변경을 하려면 많은 곳에서 코드를 변경하지 않고도 할 수 있어야 한다는 것을 의미합니다.
이상적으로는 새로운 코드를 추가하고, 오래된 코드를 거의 또는 전혀 변경하지 않아도 새로운 기능을 구현할 수 있어야 코드 개발 및 유지보수가 용이하기 때문입니다.
그러나, 어떤 코드도 모든 변화에 완전히 개방될 수 없기 때문에 어떤 변화일 경우에 이러한 원칙을 지원할지 결정해야 합니다.
코드를 작성할때, 잠재적인 변화가 무엇인지 생각하고, 어떤 것을 지원할지 결정이 필요합니다.


OCP를 준수해야하는 이유


- OCP를 준수하면 소프트웨어를 유지보수하기가 더 쉽습니다.
- OCP를 정말 잘 지켰다면 기존코드를 건드리지 않아도 되며, 기존코드에 대해 테스트가 검증이 되었으므로 새로운기능만 잘 테스트하면 됩니다.
- 기존코드를 건드리지 않는다면, 그만큼 변경에 대한 side effect나 버그가 발생될 확률이 낮아지게 됩니다.



OCP 도입의 예


아래와 같이 도형의 면적을 구하는 코드에 대한 예제를 생각해보겠습니다.



// package shape
public class Rectangle{
 public double length;
 public double width;
}

public class Circle{
 public double radius; 
}


// package calculator
public class AreaCalculator{
 public double calculateRectangleArea(Rectangle rectangle){
 return rectangle.length *rectangle.width;
 }
 public double calculateCircleArea(Circle circle){
 return 3.14*circle.radius*circle.radius;
 } 
}




// package main
public static void main(){
 Rectangle r = new Rectangle();
 r.length = 1, r.width = 1;
 
 Circle c = new Circle();
 c.radius = 1;
 
 AreaCalculator calculator = new AreaCalculator();
 System.out.println(calculator.calculateRectangleArea(r));
 System.out.println(calculator.calculateCircleArea(c));
}
 


위에서 AreaCalculator는 도형을 인자로 받으면, 그 도형에 따라 면적을 출력해주는 역할을 합니다.

도형의 종류가 추가되어, AreaCalculator가 이를 수행하게 하려면 어떻게 작성되어야 할까요?
예를 들어, 삼각형의 면적을 구하려고 한다면 아래와 같이 수정할 수 있을 것입니다.



// package shape
public class Rectangle{
 public double length;
 public double width;
}

public class Circle{
 public double radius; 
}

public class Triangle{ // 변경된 line
 public double length; // 변경된 line
 public double width; // 변경된 line
} // 변경된 line


// package calculator
public class AreaCalculator{
 public double calculateRectangleArea(Rectangle rectangle){
 return rectangle.length *rectangle.width;
 }
 public double calculateCircleArea(Circle circle){
 return 3.14*circle.radius*circle.radius;
 } 
 public double calculateTriangleArea(Triangle triangle){ // 변경된 line
 return 3rectangle.length *rectangle.width / 2; // 변경된 line
 } // 변경된 line
}


// package main
public static void main(){
 Rectangle r = new Rectangle();
 r.length = 1, r.width = 1;
 
 Circle c = new Circle();
 c.radius = 1;
 
 Triangle t = new Triangle(); // 변경된 line
 t.width = 1; // 변경된 line
 t.length = 1; // 변경된 line
 
 AreaCalculator calculator = new AreaCalculator();
 System.out.println(calculator.calculateRectangleArea(r));
 System.out.println(calculator.calculateCircleArea(c));
 System.out.println(calculator.calculateCircleArea(t)); // 변경된 line
}



위의 예제에서 아래와 같은 open-closed 원칙을 세워보겠습니다.
open : 도형은 추가될 수 있다.
closed : 도형이 추가되더라도 calculator 클래스는 변화하지 않아야한다.

calculator는 어떤 모양이든 받아들일 수 있도록 하기위해,
shape라는 인터페이스를 만들어보겠습니다.
그러면 아래와 같이 코드를 개선할 수 있습니다.






// package shape
interface shape{
 public double calculateArea();
}
public class Rectangle implements shape{
 public double length;
 public double width;
 public double calculateArea(){
 return rectangle.length *rectangle.width;
 }
}

public class Circle implements shape{
 public double radius; 
 public double calculateArea(){
 return 3.14*circle.radius*circle.radius;
 }
}

public class Triangle implements shape{ 
 public double length; 
 public double width; 
 public double calculateArea(){ 
 return rectangle.length *rectangle.width/2; 
 } 
} 


// package calculator
public class AreaCalculator{
 public double calculateArea(Shape shape){
 return shape.calculateArea();
 }
}


// package main
public static void main(){
 Rectangle r = new Rectangle();
 r.length = 1, r.width = 1;
 
 Circle c = new Circle();
 c.radius = 1;
 
 Triangle t = new Triangle(); // 변경된 line
 t.width = 1; // 변경된 line
 t.length = 1; // 변경된 line
 
 AreaCalculator calculator = new AreaCalculator();
 System.out.println(calculator.calculateArea(r));
 System.out.println(calculator.calculateArea(c));
 System.out.println(calculator.calculateArea(t)); 
}



코드 줄 수가 좀 늘어났지만, 유연성이 증가해져서, 변화시 컴파일도 덜 해도된다는 장점이 있습니다.
첫번째 코드대로 한다면 변화에 대해 3가지 package(shape, calculator, main)가 변경이 되어야 하지만,
마지막 코드대로 한다면 calcuator 라는 class는 다시 컴파일하지 않아도 되는 장점이 있습니다.
파일이 한 두개 일때는 큰 장점이 아니지만, 프로젝트 규모가 커지고 라이브러리화 하면 이러한 장점이 크게 나타나게 됩니다.


총평


어떤 부분을 닫히게 할지, 어떤 부분을 열리게 할지는 오랜 개발 경험이 필요할 것이라 어려운 원칙일 수도 있습니다.
OCP는 기본적으로 SRP와 연관되어 있다고 볼 수도 있습니다.
SRP를 잘 지킨다면 자연스럽게 그 클래스의 역할부분만 바뀌었을때,
딱 그 클래스만 바꾸면 되는 open closed principle과 의미상 상통하기 때문입니다.
하지만 SRP역시 어느정도 개발 경험이 있어야 잘 수행할 수 있기도 합니다.
결론적으로는 SRP든 OCP든 다른 코드들을 잘 살펴보고,
변경사항이 많은 프로젝트 개발경험을 많이 쌓으면 자연스럽게 잘 지킬 수 있게 되지 않을까 싶습니다.



#SOLID,#SOLID원칙,#개방,#폐쇄,#원칙,#SOLIDprinciple,#OCP,#개방폐쇄원칙,#open,#closed,#principle

 

https://devscb.com/post/95

 

728x90
반응형

댓글