Swift/Design_Pattern

[Swift 디자인 패턴] Strategy Pattern (전략) - 디자인 패턴 공부 22

Dev_Pingu 2021. 6. 30. 22:00
반응형

안녕하세요 Pingu입니다!🐧

 

지난 글에서는 행동 패턴 중 하나인 State Pattern(상태)에 대해 알아봤었는데요, 이번 글에서는 또 다른 행동 패턴 중 하나인 Strategy Pattern에 대해 알아보도록 하겠습니다.

전략 패턴이란?

Strategy Pattern(전략)은 Algorithm Family를 정의하고 각 알고리즘을 캡슐화 한 뒤 런타임에서 알고리즘을 서로 바꿔 사용할 수 있는 디자인 패턴입니다. 

 

  • Strategy (Compositor)
    • 지원되는 모든 알고리즘에 사용되는 공통적인 인터페이스를 정의합니다.
    • Context는 Strategy 인터페이스를 사용하여 Concrete Strategy에 정의된 알고리즘을 호출합니다.
  • Concrete Strategy
    • Strategy 인터페이스를 사용하여 알고리즘을 구현합니다.
  • Context (Composition)
    • Concrete Strategy 객체로 구성됩니다.
    • Strategy 객체에 대한 참조를 유지합니다.
    • Strategy가 데이터에 접근 할 수 있는 인터페이스를 정의합니다.

전략 패턴은 언제 쓰나요?

전략 패턴은 어떤 상황에서 사용할 알고리즘이 여러 개 존재할 수 있을 때 사용하면 좋습니다. 알고리즘을 런타임에서 바꿀 수 있기 때문에 이와 같은 상황에 효과적으로 대응할 수 있습니다.

 

예를 들어 네비게이션을 개발했다고 해볼게요. 처음 만들 땐 자동차의 경로만 알려주는 내비게이션을 만들었습니다. 그러다 사용자가 늘어 이젠 도보로 이동하는 사람의 경로, 대중교통을 이동하는 사람의 경로와 같이 다양한 알고리즘이 필요하게 되었습니다. 이를 위해 클래스 안에 계속해서 구현하다 보면, 하나의 변경으로 인해 많은 곳을 수정해야 하는 문제가 발생합니다.

 

이를 해결하기 위해 전략 패턴을 사용 할 수 있습니다. 자동차의 경로, 도보로 이동하는 경로, 대중교통 경로에 대한 알고리즘을 따로 구현하고 이들을 캡슐화합니다. 각각의 알고리즘을 서로에게 영향을 주지도 않고 각자 독립적으로 작동하도록 만들어주는 것이죠. 이렇게 하면 아까와 같은 문제를 해결할 수 있습니다!

전략 패턴의 결과

장점

  • 런타임에서 객체 내부에서 사용되는 알고리즘을 변경 할 수 있습니다.
  • 알고리즘을 사용하는 코드와 알고리즘을 구현하는 코드를 분리할 수 있습니다.
  • Open / Closed Principle(개방 폐쇄 원칙)을 준수합니다. Context를 변경하지 않고도 새로운 Strategy를 도입할 수 있습니다.

단점

  • 알고리즘이 몇 개 없고 변경되는 일도 거의 없는 경우 전략 패턴의 도입이 오히려 복잡성을 증가시킬 수 있습니다.
  • 클라이언트가 적절한 Strategy를 선택하기 위해서는 각각의 차이점을 알고 있어야 합니다.
  • Strategy, Context간 통신 오버헤드가 발생합니다.

예제

그럼 이제 Strategy Pattern을 Swift로 구현해보겠습니다.

 

아까 본 네비게이션 문제를 구현해보면 재밌을 거 같으니 한 번 해보도록 하겠습니다.

먼저 Strategy 인터페이스 역할을 할 프로토콜을 하나 정의해줍니다.

// Strategy
protocol Strategy {
    func algorithmExecute()
}

이번에는 Strategy 프로토콜을 채택하는 알고리즘들을 만들어주면 됩니다.

저는 네비게이션으로 자동차, 도보, 자전거 경로를 알고 싶으니 Strategy 프로토콜을 채택한 3개의 클래스를 만들어줍니다.

// Concrete Strategy
class CarRoute: Strategy {
    func algorithmExecute() {
        print("자동차 경로 찾기 완료!\n")
    }
}

// Concrete Strategy
class WalkRoute: Strategy {
    func algorithmExecute() {
        print("도보 경로 찾기 완료!\n")
    }
}

// Concrete Strategy
class BikeRoute: Strategy {
    func algorithmExecute() {
        print("자전거 경로 찾기 완료!\n")
    }
}

 

그런 뒤 만든 알고리즘들을 교체해가며 사용할 Context를 구현합니다.

// Context
class Navigation {
    private var routeAlgorithm: Strategy?
    
    func execute() {
        self.routeAlgorithm?.algorithmExecute()
    }
    
    func setStrategy(strategy: Strategy) {
        self.routeAlgorithm = strategy
    }
}

Strategy 객체를 참조하고 있는 Context 클래스를 만들었다면 이제 모두 구현했습니다.

그럼 실제로 사용해볼게요.

위의 결과를 보면 런타임에서 계속해서 Strategy 객체를 변경하며 다른 알고리즘을 사용하는 것을 볼 수 있습니다.

 

이렇게 Strategy Pattern을 알아보고 간단하게 구현도 해봤습니다.

혹시라도 틀린 부분이 있다면 알려주시면 감사하겠습니다.

 

전체 코드는 여기에서 볼 수 있습니다.

 

감사합니다.

반응형