본문 바로가기
Spring Study

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

by xogns93 2024. 8. 5.

OCP(Open-Closed Principle, 개방-폐쇄 원칙)은 객체 지향 프로그래밍의 SOLID 원칙 중 하나로, 소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다는 원칙을 의미합니다. 즉, 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있도록 설계해야 한다는 것입니다.

 

OCP의 주요 개념

  1. 확장에 열려 있어야 한다(Open for extension): 새로운 기능이나 요구사항이 생길 때, 기존 코드를 변경하지 않고 확장할 수 있어야 합니다.
  2. 변경에 닫혀 있어야 한다(Closed for modification): 기존의 잘 작동하는 코드를 변경하지 않고도 새로운 기능을 추가할 수 있어야 합니다. 이를 통해 기존 기능의 안정성을 유지할 수 있습니다.


OCP는 소프트웨어 개발에서 변경에 대한 비용과 위험을 최소화하기 위해, 기존의 코드를 수정하지 않고도 기능을 추가하거나 변경할 수 있도록 하는 것을 목표로 합니다. OCP는 다음과 같은 내용을 담고 있습니다.

  • 개방(O): 모듈은 확장에 대해 열려 있어야 합니다. 즉, 새로운 요구사항이나 기능이 추가되어도 기존 코드의 수정 없이도 새로운 코드를 추가할 수 있어야 합니다. UserDao는 DB 커넥션 기능을 확장하는 데는 오픈되어 있습니다(DConnectionMaker, NConnectionMaker). UserDao에 전혀 영향을 주지 않고도 얼마든지 기능을 확장할 수 있게 되었습니다.
  • 폐쇄(C): 모듈은 수정에 대해 닫혀 있어야 합니다. 즉, 기존 코드를 수정하지 않고도 모듈의 기능을 확장하거나 변경할 수 있어야 합니다. UserDao 자신의 핵심 기능을 구현한 코드는 변경(DB 커넥션 기능을 확장)에 영향을 받지 않고 유지할 수 있으므로 변경에는 닫혀 있다고 말할 수 있습니다.

 

 

위 이미지는 OCP를 가장 잘 보여주는 그림입니다.

UserDao의 ConnectionMaker 인터페이스를 통해 제공되는 확장 포인트는 확장을 위해 Open 되어 있지만, 이러한 확장을 위해 UserDao 자신의 변화가 불필요하게 일어나지 않도록 Close되어 있습니다.

 

이러한 OCP를 지키기 위해 다양한 디자인 패턴이 존재하며, 상속, 추상화, 인터페이스 등의 개념을 활용하여 객체 간의 결합도를 낮추고, 유연하고 확장 가능한 설계를 만들어냅니다. OCP는 소프트웨어 개발에서 중요한 원칙 중 하나이며, 유지보수와 확장성을 고려한 잘 설계된 소프트웨어를 만드는 데에 큰 역할을 합니다.

  • 추상화: 추상화를 사용하여 코드의 일부를 인터페이스나 추상 클래스로 분리하고, 이를 기존 코드와 분리합니다. 이렇게 분리된 코드는 변경에 대해 폐쇄적이며, 기능을 추가할 때는 새로운 클래스를 만들어서 이를 구현하면 됩니다.
  • 다형성: 다형성을 사용하여, 인터페이스나 추상 클래스에 대한 구현 클래스를 다양하게 만들 수 있습니다. 이렇게 구현된 클래스는 기존 코드에 영향을 주지 않으면서 기능을 확장할 수 있습니다.
  • 상속: 상속을 사용하여, 부모 클래스를 확장하여 새로운 클래스를 만들 수 있습니다. 이렇게 만들어진 새로운 클래스는 기존 코드와는 독립적으로 동작하므로, 기능을 확장할 때 영향을 주지 않습니다.

다음 코드는 OCP가 적용되지 않은 예시입니다.

class Rectangle {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getArea() {
        return width * height;
    }
}

class Circle {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }
}

class AreaCalculator {
    public double calculateTotalArea(Object[] shapes) {
        double totalArea = 0;
        for (Object shape : shapes) {
            if (shape instanceof Rectangle) {
                Rectangle rectangle = (Rectangle) shape;
                totalArea += rectangle.getArea();
            } else if (shape instanceof Circle) {
                Circle circle = (Circle) shape;
                totalArea += circle.getArea();
            }
            // 다른 도형이 추가될 때마다 이곳에 새로운 조건을 추가해야 합니다.
        }
        return totalArea;
    }
}

 

다음은 OCP를 적용한 Java 코드 예시입니다. 이 예시는 추상화를 통해 OCP를 지키는 방법을 보여줍니다.

// Abstract class for Shapes
abstract class Shape {
  public abstract double area();
}

// Concrete class for Circle
class Circle extends Shape {
  private double radius;

  public Circle(double radius) {
    this.radius = radius;
  }

  public double area() {
    return Math.PI * radius * radius;
  }
}

// Concrete class for Rectangle
class Rectangle extends Shape {
  private double width;
  private double height;

  public Rectangle(double width, double height) {
    this.width = width;
    this.height = height;
  }

  public double area() {
    return width * height;
  }
}

// Client class to calculate areas of shapes
class AreaCalculator {
  public double calculateArea(Shape shape) {
    return shape.area();
  }
}

 

위 코드에서 Shape 클래스는 추상 클래스로, area() 메소드는 추상 메소드입니다. Circle과 Rectangle 클래스는 이 Shape 클래스를 상속받으며, 각 도형의 넓이를 계산하는 area() 메소드를 구현합니다.

AreaCalculator 클래스는 도형의 넓이를 계산하는 클래스입니다. 이 클래스는 Shape 클래스의 인스턴스를 인수로 받아 해당 도형의 넓이를 계산합니다.

위 코드에서 OCP를 지키는 방법은 다음과 같습니다.

  • Shape 클래스는 추상 클래스로, 도형의 공통점을 정의합니다.
  • Circle과 Rectangle 클래스는 이 추상 클래스를 상속받아 각 도형의 특성을 구현합니다.
  • AreaCalculator 클래스는 Shape 클래스의 인스턴스를 인수로 받기 때문에, Circle과 Rectangle 클래스의 인스턴스를 모두 처리할 수 있습니다. 이렇게 함으로써, AreaCalculator 클래스는 Circle과 Rectangle 클래스의 변경 없이도[Close] 새로운 도형 클래스를 추가[Open]할 수 있습니다.

따라서, 위 코드는 OCP를 지키면서 확장성을 높인 코드입니다. 새로운 도형 클래스가 추가되어도 기존 코드를 변경할 필요가 없으며, 코드의 유지보수와 확장성이 용이합니다.

 

OCP의 장점

  1. 유지보수 용이성: 새로운 기능을 추가할 때 기존 코드를 변경하지 않으므로, 코드의 버그가 발생할 가능성이 줄어듭니다.
  2. 유연한 설계: 코드가 확장 가능하도록 설계되기 때문에 새로운 요구사항을 쉽게 반영할 수 있습니다.
  3. 테스트 용이성: 코드 변경이 적으므로 테스트해야 할 범위가 줄어들어 테스트가 더 쉬워집니다.

이처럼 OCP를 통해 소프트웨어 모듈을 설계하면, 변경이 최소화되고 확장성이 높아져 더욱 견고한 시스템을 구축할 수 있습니다.