ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 개방-폐쇄 원칙(Open-Closed Principle)
    기록/OOP 2023. 1. 28. 21:32

     

    SOLID개방-폐쇄 원칙(Open-closed principle)은 소프트웨어 객체는 확장에 대해 열려있어야 하고, 수정에 대해서는 닫혀있어야 함을 말합니다.

     

    다음은 개방-폐쇄 원칙을 위배하는 코드입니다.

    class BallGame {
        let name: String
        
        init(_ name: String) {
            self.name = name
        }
    }
    
    func play(ballGame: BallGame) {
        switch ballGame.name {
        case "Soccer":
            print("11 vs 11")
        case "BaseBall":
            print("9 vs 9")
        default:
            fatalError("Not implemented.")
        }
    }
    
    let soccer = BallGame("Soccer")
    let baseBall = BallGame("BaseBall")
    play(ballGame: soccer)
    play(ballGame: baseBall)

    위의 코드에서 또 다른 구기종목인 탁구를 추가하고 싶다면, BallGame 타입으로 해당하는 변수를 추가해주고, play() 함수까지 수정해줘야 합니다. 확장하기 위해 기존의 코드를 수정해야 하는 상황이 발생하는 것입니다.

     

    Swift에서는 강력한 추상화 도구인 protocol을 사용하여 기존의 코드를 수정하지 않고 확장할 수 있도록 만들어줄 수 있습니다.

    protocol BallGame {
        func play()
    }
    
    class Soccer: BallGame {
        func play() {
            print("11 vs 11")
        }
    }
    
    class BaseBall: BallGame {
        func play() {
            print("9 vs 9")
        }
    }
    
    class PingPong: BallGame {
        func play() {
            print("1 vs 1 or 2 vs 2")
        }
    }
    
    func play(ballGame: BallGame) {
        ballGame.play()
    }
    
    let soccer = Soccer()
    let baseBall = BaseBall()
    let pingPong = PingPong()
    
    play(ballGame: soccer)
    play(ballGame: baseBall)
    play(ballGame: pingPong)

     위와 같이 프로토콜을 만들어서 그것을 각 클래스가 준수하도록 만들어주면, 이미 잘 동작하던 함수는 수정할 필요 없이 확장에 대한 코드만 추가로 작성해주면 됩니다. 예시 코드는 아주 적은 양의 코드로 단순하게 작성되어 있어서 와닿지 않을 수도 있습니다. 하지만 규모가 커짐에 따라 개방-폐쇄 원칙을 따르지 않고 모든 코드를 작성하게 되면 모듈 하나를 수정할 때마다, 엮여있는 모든 것들을 일일이 수정해야 합니다. 이는 객체 지향 프로그래밍의 장점인 유연성, 재사용성, 유지보수성을 얻어낼 수 없는 것입니다. 따라서 지켜야 할 원칙 중 하나입니다.

     


     추가로, Swift에서 아래의 코드와 같이 프로토콜 채택을 클래스로만 제한할 수도 있습니다. 그렇게 하면 컴파일러가 클래스만 프로토콜을 채택할 것이라는 정보를 기반으로 프로그램을 최적화할 수 있다는 장점이 있으므로 런타임에서 더 나은 성능을 누릴 수 있습니다. '클래스만 이 프로토콜을 채택할 것이다.'라는 정보가 없다면 컴파일러는 struct가 이 프로토콜을 충족할 수 있다고 가정해야 하고, 거기에서 비용이 발생합니다.

    protocol BallGame: AnyObject {
        func play()
    }

    참고

    Apple github

    (https://github.com/apple/swift/blob/main/docs/OptimizationTips.rst#id11)

    댓글

Designed by Tistory.