1. 개방 폐쇄 원칙이란
•
소프트웨어 컴포넌트는 확장에 열려 있어야 하고 수정에 관해서는 닫혀 있어야 한다는 의미를 말한다.
•
다른 개발자가 작업을 수행하기 위해서는 반드시 수정해야 하는 제약 사항을 클래스에 포함해서는 안 된다는 사실을 의미한다.
•
다양하고 직관적이며 유해하지 않은 방식으로 소프트웨어 확장성을 유지하는 원칙이다.
2. 예시
개방-폐쇄 원칙을 따르지 않는 경우
•
Shape
package challenge.bad;
public interface Shape {}
Java
복사
•
Rectangle
package challenge.bad;
public class Rectangle implements Shape {
private final int width;
private final int weight;
public Rectangle(int width, int weight) {
this.width = width;
this.weight = weight;
}
public int getWidth() {
return width;
}
public int getWeight() {
return weight;
}
}
Java
복사
•
Circle
package challenge.bad;
public class Circle implements Shape {
private final int radius;
public Circle(int radius) {
this.radius = radius;
}
public int getRadius() {
return radius;
}
}
Java
복사
이 코드에서 클래스의 생성자를 활용하여 서로 다른 크기의 직사각형과 원을 쉽게 만들 수 있다. 여러 가지 도형을 만들었다면 모든 면적을 합할 차례이다. 면적의 합을 구하기 위해 AreaCalculator 클래스를 다음과 같이 정의한다.
•
AreaCalculator
package challenge.bad;
import java.util.List;
public class AreaCalculator {
private final List<Shape> shapes;
public AreaCalculator(List<Shape> shapes) {
this.shapes = shapes;
}
/**
* 도형을 추가하려면 이 클래스를 수정해야 한다.
* 이 코드는 개방-폐쇄 원칙에 맞지 않는다.
*/
public double sum() {
int sum = 0;
for (Shape shape : shapes) {
if (shape.getClass().equals(Circle.class)) {
sum += (int) (Math.PI * Math.pow(((Circle) shape).getRadius(), 2));
} else if (shape.getClass().equals(Rectangle.class)) {
sum += ((Rectangle) shape).getHeight() * ((Rectangle) shape).getWidth();
}
}
return sum;
}
}
Java
복사
각 도형은 면적을 구하기 위한 고유의 공식이 있으므로 도형의 유형을 구분하려면 if-else 또는 switch 구조가 필요하다. 또한 삼각형과 같은 새로운 도형을 추가하고 싶다면 새로운 if문을 추가하기 위해 AreaCalculator 클래스를 수정해야 한다. 이것은 이 코드가 개방-폐쇄 원칙을 위반한다는 것을 의미한다. 개방-폐쇄 원칙을 준수하도록 코드를 수정하려면 모든 클래스에서 몇 가지를 수정해야 한다.
개방-폐쇄 원칙을 따르는 경우
AreaCalculator에서 도형의 면적 계산 공식을 빼서 공식이 필요한 각 Shape 클래스로 옮기는 것이다. 즉 직사각형, 원 등의 각 도형에 해당하는 클래스에서 자신의 면적을 계산하도록 하는 것이다. 각 도형 클래스에서 면적을 계산하도록 Shape 인터페이스 정의에 area 메서드를 추가한다.
•
Shape
package challenge.good;
public interface Shape {
public double area();
}
Java
복사
•
Circle
package challenge.good;
public class Circle implements Shape {
private final int radius;
public Circle(int radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * Math.pow(radius, 2);
}
}
Java
복사
•
Rectangle
package challenge.good;
public class Rectangle implements Shape {
private final int width;
private final int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
Java
복사
•
AreaCalculator
package challenge.good;
import java.util.List;
public class AreaCalculator {
private final List<Shape> shapes;
public AreaCalculator(List<Shape> shapes) {
this.shapes = shapes;
}
public double sum() {
int sum = 0;
for (Shape shape : shapes) {
sum += (int) shape.area();
}
return sum;
}
}
Java
복사
이 코드는 개방-폐쇄 원칙을 따른다. AreaCalculator 클래스를 수정하지 않고도 새로운 도형을 추가할 수 있기에 수정에 관해 닫혀 있으면서 확장에 관해서는 열려 있다.