Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

DailyCode

[SOLID] 개방 폐쇄 원칙(OCP) 본문

디자인 패턴

[SOLID] 개방 폐쇄 원칙(OCP)

JSKoder 2023. 3. 24. 18:52

정의

소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하지만 변경에는 닫혀 있어야 한다는 개념입니다.

이는 기존의 코드를 변경하지 않고도 새로운 기능을 추가할 수 있도록 하는 것을 목표로 합니다.

이를 위해서는 클래스와 인터페이스를 확장 가능하도록 설계해야 합니다.

장점

  • 기능 추가에 용이

OCP를 따르는 코드는 기능 추가 시 기존 코드를 변경하지 않아도 되므로 유연성이 높습니다.

  • 클래스를 분리 테스트 할 수 있어 개별 부분에 대한 테스트가 용이

OCP를 따르는 코드는 기능 추가 시 기존 코드를 변경하지 않아도 되므로 유연성이 높습니다.

 

예제

  • 도형의 너비를 구하는 클래스를 예로 들어보겠습니다.

이 클래스는 도형의 종류에 따라 너비를 구하는 메소드를 제공합니다.

만약 새로운 도형이 추가되면, 이전 코드를 수정하고 메소드를 추가해야 합니다.

이 경우 기존 코드를 수정하는 것은 OCP에 어긋납니다.

이를 해결하기 위해, 새로운 도형이 추가될 때 기존 코드를 수정하지 않고 새로운 클래스를 추가하면 됩니다.

이렇게 하면 확장에는 열려 있으며 변경에는 닫혀 있는 OCP를 따르게 됩니다.

//도형의 너비 구하는 메서드를 프로토콜로 빼고, 너비를 구하고 싶으면 프로토콜 상속받아서 사용(다른 도형을 추가할 경우 기존 소스를 건드릴 필요 없이 클래스 생성해서 구현만 해주면 됨)
class Shape {
    enum ShapeType {
        case circle
        case square
        //MORE..
    }
    var width: CGFloat
    var height: CGFloat
    var radius: CGFloat
    init(width: CGFloat, height: CGFloat, radius: CGFloat) {
        self.width = width
        self.height = height
        self.radius = radius
    }
    
    func calculate(_ type: ShapeType) -> CGFloat {
        switch type {
        case .circle:
            return width * height
        case .square:
            return CGFloat.pi * pow(radius, 2)
        }
    }
}

Refactoring

//도형의 너비 구하는 메서드를 프로토콜로 빼고, 너비를 구하고 싶으면 프로토콜 상속받아서 사용(다른 도형을 추가할 경우 기존 소스를 건드릴 필요 없이 클래스 생성해서 구현만 해주면 됨)
protocol Shape {
    func calculate() -> CGFloat
}

// Circle 클래스는 Shape 프로토콜을 적용하여 calculateArea() 메서드를 구현함
class Circle: Shape {
    var radius: CGFloat
    
    init(radius: CGFloat) {
        self.radius = radius
    }
    
     func calculate() -> CGFloat {
        return Double.pi * pow(radius, 2)
    }
}

// Rectangle 클래스는 Shape 프로토콜을 적용하여 calculateArea() 메서드를 구현함
class Rectangle: Shape {
    var width: CGFloat
    var height: CGFloat
    
    init(width: CGFloat, height: CGFloat) {
        self.width = width
        self.height = height
    }
    
     func calculate() -> CGFloat {
        return width * height
    }
}

 

결론

이렇게 함으로써, 클래스의 기존 동작을 변경하지 않으면서(변경에는 닫혀 있고) 클래스의 기능을 추가(확장에는 열려있다.)할 수 있습니다.

 

학습하면서 배운 느낀점

SI 근무를 하면서 OCP를 따르는 코드 작성의 중요성을 깨닫게 되었습니다.

작업을 진행하다 보면 고객사로부터 수정 요청이나 추가 개발 요청이 필연적으로 발생합니다.

이런 상황에서 컨트롤러에 코드를 단순히 추가하기만 한다면, 나중에 또 다른 요청이 들어올 때 소스 코드의 복잡성이 증가하게 됩니다. 이로 인해 기존 코드에 영향을 주게 되고, 전체 소스를 수정해야 하는 상황이 발생하여 작업량이 급격하게 증가할 수 있습니다.

불필요한 일을 방지하고자 한다면. 설계 전에 반드시 재사용과 결합성을 고려해서 설계했으면 좋겠습니다.