ABOUT ME

개발지식, 투자, 여행, 책 영화 리뷰

Today
Yesterday
Total
  • [디자인패턴] 개방-폐쇄 원칙 OCP, Open-Closed Principle
    개발지식 아카이브/Algorithms 2025. 2. 17. 07:00

     
     

    Open-Closed Principle, OCP

     

    저는 오늘 회사에서 OCP 원칙을 위반하는 코드를 찾아서 수정 제안을 드렸습니다. 그래서 오랜만에 SOLID 패턴 복습하면서, 디자인 패턴 포스팅을 작성합니다!
     
    개방-폐쇄 원칙은 SOLID 원칙 중 하나로, "소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고, 변경(수정)에는 닫혀 있어야 한다"는 개념입니다.
    즉, 기존 코드를 수정하지 않고 새로운 기능이나 동작을 추가할 수 있도록 설계해야 한다는 뜻입니다. 이 원칙을 잘 지키면 시스템을 확장할 때 기존에 안정적으로 동작하던 부분을 변경할 필요가 없어 유지보수가 쉬워집니다.
     
     
     


     
     
     

    개방-폐쇄 원칙의 필요성

    프로젝트가 커지면서 기능을 추가하거나 수정해야 할 일이 많아지면, 기존 코드에 직접 손을 대게 됩니다.
    이렇게 되면 새로운 버그가 발생할 위험이 있고, 여러 모듈이 얽혀 있는 경우 [하나의 변경의 영향 범위]가 커지게 됩니다.
    하지만 OCP를 따르면 새로운 기능은 기존 코드를 건드리지 않고 추가하는 방식으로 설계할 수 있으므로, 안정성과 확장성을 모두 확보할 수 있습니다.
     
     
     


     

    OCP를 위반한 예시

    아래는 OCP를 위반한 코드 예시입니다. 여기서는 도형(Shape)의 면적을 계산하는 기능을 구현하고 있는데, 새로운 도형이 추가될 때마다 calculate_area() 함수를 수정해야 합니다.
     

    # OCP 위반 예시: 새로운 도형이 추가될 때마다 함수 수정 필요
    class Rectangle:
        def __init__(self, width, height):
            self.width = width
            self.height = height
    
    class Circle:
        def __init__(self, radius):
            self.radius = radius
    
    def calculate_area(shape):
        if isinstance(shape, Rectangle):
            return shape.width * shape.height
        elif isinstance(shape, Circle):
            import math
            return math.pi * shape.radius ** 2
    
    # 사용 예시
    rect = Rectangle(3, 4)
    circle = Circle(5)
    print("Rectangle area:", calculate_area(rect))
    print("Circle area:", calculate_area(circle))
     
     
     

    위 코드에서는 새로운 도형 예를 들어 Triangle 클래스를 추가하려면 calculate_area() 함수에 조건문을 추가하는 등 기존 코드를 수정해야 하므로 OCP 원칙에 어긋납니다.
     
     
     


    OCP를 준수한 예시

    OCP를 준수하기 위해서는 추상화를 활용해 공통 인터페이스를 정의하고, 각 도형은 해당 인터페이스를 구현하도록 설계합니다.
    이렇게 하면 새로운 도형을 추가할 때 기존의 계산 로직을 변경하지 않고, 새로운 클래스만 추가하면 됩니다.
     
     

     
     

    from abc import ABC, abstractmethod
    import math
    
    # 공통 인터페이스(추상 클래스) 정의
    class Shape(ABC):
        @abstractmethod
        def area(self):
            """도형의 면적을 계산하여 반환하는 메서드"""
            pass
    
    # Rectangle 클래스: Shape 인터페이스 구현
    class Rectangle(Shape):
        def __init__(self, width, height):
            self.width = width
            self.height = height
    
        def area(self):
            return self.width * self.height
    
    # Circle 클래스: Shape 인터페이스 구현
    class Circle(Shape):
        def __init__(self, radius):
            self.radius = radius
    
        def area(self):
            return math.pi * self.radius ** 2
    
    # 새로운 도형: Triangle 클래스 추가 (기존 코드를 수정할 필요 없이 확장)
    class Triangle(Shape):
        def __init__(self, base, height):
            self.base = base
            self.height = height
    
        def area(self):
            return 0.5 * self.base * self.height
    
    # 도형들의 총 면적을 계산하는 함수
    def total_area(shapes):
        return sum(shape.area() for shape in shapes)
    
    # 사용 예시
    shapes = [
        Rectangle(3, 4),
        Circle(5),
        Triangle(6, 8)
    ]
    print("Total area:", total_area(shapes))

     
     

     
     

    포인트 정리:

    • 확장에는 열려 있음: 새로운 클래스 Triangle을 추가할 때 기존의 total_area() 함수나 다른 클래스의 코드를 전혀 수정하지 않고, 단지 새로운 클래스를 추가하여 기능을 확장할 수 있습니다.

     

    • 변경에는 닫혀 있음: 기존에 작성한 Shape 인터페이스와 각 도형 클래스는 변경 없이 그대로 유지됩니다.

     
     
    이처럼 OCP를 잘 적용하면, 새로운 요구 사항이나 기능 추가 시 기존 코드를 건드리지 않아도 되므로 안정성과 유지보수성이 크게 향상됩니다.
     
     


     
     
     

    결론

    개방-폐쇄 원칙은 소프트웨어 설계 시 유연성과 확장성을 확보하는 중요한 원칙입니다.
    새로운 기능이 추가되더라도 기존의 안정적인 부분에 영향을 주지 않도록 클래스를 설계함으로써, 복잡한 시스템에서도 유지보수를 쉽게 할 수 있습니다.

    댓글

Copyright in 2020 (And Beyond)