yongmin.Lee 2022. 4. 15. 15:00

Strategy Pattern

 

 

Strategy Pattern 이란?

Strategy Pattern (전략 패턴)은 교환 가능한 객체들을 정의해두고 Strategy protocol을 이용하여 런타임에 설정하거나 변환하는 패턴

Object using a Strategy : Strategy protocol을 이용하는 객체

Strategy protocol : 모든 strategy가 반드시 구현해야 할 메서드들을 정의하고 있는 프로토콜

Strategies : Strategy 프로토콜을 준수하는 교환가능한 객체들

 

Strategy Pattern 예시코드

// MARK: - Strategy Protocol
protocol MovieRatingStrategy {
    var ratingServiceName: String { get }
    
    func fetch(fot title: String, success: @escaping ((_ rating: String, _ review: String) -> Void))
}

// MARK: - Concrete Strategy 1
class RottenTomatoesClient: MovieRatingStrategy {
    let ratingServiceName: String = "Rotten Tomatoes"
    
    func fetch(fot title: String, success: @escaping ((String, String) -> Void)) {
        // 통신을 통해 값을 받아온다고 생각합니다.
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            let rating = "95%"
            let review = "It rocked!"
            success(rating, review)
        }
    }
}

// MARK: - Concrete Strategy 2
class IMDbClient: MovieRatingStrategy {
    let ratingServiceName: String = "IMDb"
    
    func fetch(fot title: String, success: @escaping ((String, String) -> Void)) {
        // 통신을 통해 값을 받아온다고 생각합니다.
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            let rating = "3 / 10"
            let review = "It was terrible!"
            success(rating, review)
        }
    }
}

// MARK: - Object using a Strategy
class ViewController: UIViewController {
    
    var movieRatingClient: MovieRatingStrategy!
    
    let ratingServiceNameLabel = UILabel()
    let ratingLabel = UILabel()
    let reviewLabel = UILabel()
    
    init(movieRatingClient: MovieRatingStrategy) {
        self.movieRatingClient = movieRatingClient
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        ratingServiceNameLabel.text = movieRatingClient.ratingServiceName
        
        movieRatingClient.fetch(fot: "movie title") { rating, review in
            self.ratingLabel.text = rating
            self.reviewLabel.text = review
        }
    }
}

 

Strategy Pattern 사용하는 이유

  • 정책에 따라 유연하게 동작하는 메서드를 캡슐화함으로써 정책마다 로직이 if-else 문으로 분기 하는것을 방지
  • 새로운 정책이 추가되거나 기존의 정책이 사라지더라도, 사용하는 쪽에서는 코드를 수정할 필요가 없다.
  • 기능을 사용하는 부분과 구현하는 부분을 명확히 분리한다

 

SOLID 관점에서 바라본 Strategy Pattern

  • 기능 확장에는 열려있고 변경에는 닫혀있는 개방 폐쇄 원칙(OCP)도 따르는 구조를 갖게 된다.
  • 인터페이스로 타입을 추상화하지 않았다면, 구현체를 직접 의존하게 되고 이는 두 객체간의 강한 결합으로 런타임에 메서드를 변경하지 못하게 된다. 사용객체과 구현객체 모두 추상화에 의존하게 하므로서 느슨한 결합을 가져가고 있으므로 의존성 역전 원칙(DIP)도 잘 따르고 있다고 할 수 있다.

 

 

 

 

 

참고자료

https://velog.io/@ljinsk3/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-Strategy-Pattern

https://brunch.co.kr/@yoonms/21