[디자인 패턴] Strategy Pattern

J_sung_0o0
5 min readSep 20, 2020

--

defines a family of interchangeable objects that can be set or switched at runtime.

Object using a Strategy (전략을 사용하는 객체)

iOS 에서는 주로 뷰 컨트롤러가 이에 해당합니다.

Strategy Protocol (전략 프로토콜)

모든 전략들이 반드시 구현해야 하는 메소드를 정의하고 있습니다.

Concrete Strategies (구체적인 전략)

Strategy protocol 을 구현합니다.

언제 써야 하나?

  1. interchangeable 인 2개 이상의 서로 다른 동작들을 갖고 있을 때

2. delegation 패턴과 유사점: 둘 다 프로토콜에 의존 (유연성 향상). 결과적으로 모든 객체들은 strategy 프로토콜을 구현하여 런타임 동안 strategy로 사용 가능

3. delegation 패턴과 다른점: strategy 패턴은 객체들의 family를 사용.

  • delegation 패턴은 런타임 동안 자주 delegate 들이 고정된다.
  • 하지만, strategy 들은 런타임 동안 쉽게 interchangeable 하다.

코드 예시

Strategy 프로토콜 예시

import UIKittypealias MovieRatingResult = (_ rating: String, _ review: String) -> Voidprotocol MovieRatingStrategy {// 1: 별점 서비스 이름
var ratingServiceName: String { get }
// 2: 비동기로 영화의 별점들을 가져옴.
func fetchRating(for movie: String, success: MovieRatingResult)
}

전략 구현1 예시

class RottenTomatoesClient: MovieRatingStrategy {
let ratingServiceName = "Rotten Tomatoes"
func fetchRating(for movie: String, success: MovieRatingResult) { // 가상 response
let rating = "95%"
let review = "It rocked"
success(rating, review)
}
}

전략 구현2 예시

class IMDbClient: MovieRatingStrategy {
let ratingServiceName = "IMDb"
func fetchRating(for movie: String, success: MovieRatingResult) {// 가상 response
let rating = "3 / 10"
let review = "It was terrible!"
success(rating, review)
}
}

뷰컨트롤러 예시

뷰컨트롤러가 인스턴스화 될 때마다 client를 값을 세팅해줘야 합니다. 여기서 중요한 점은 뷰컨트롤러는 MovieRatingStrategy구체적인 구현에 대해서는 알 필요가 없다는 것입니다. 어떤 전략을 사용할지는 런타임까지 미뤄질 수 있기 때문사용자가 선택을 할 수도 있습니다(앱이 허용한다는 가정하에)

class ViewController: UIViewController {    // MARK: - Properties
var client: MovieRatingStrategy!
// MARK: - Outlets
@IBOutlet weak var movieTitleTextField: UITextField!
@IBOutlet weak var ratingServiceNameLabel: UILabel!
@IBOutlet weak var ratingLabel: UILabel!
@IBOutlet weak var reviewLabel: UILabel!
// MARK: - 뷰 라이프 사이클
override func viewDidLoad() {
super.viewDidLoad()
ratingServiceLabel.text = client.ratingServiceName
}
// MARK: - Actions
@IBAction func didTapSearch() {
guard let movieTitle = movieTitleTextField.text else { return }

self.client.fetchRating(for: movieTitle) {
[self] rating, review in
self.ratingLabel.text = rating
self.reviewLabel.text = review
}
}
}

주의해야 할 점

이 패턴을 과하게 사용하지 않도록 주의해야합니다 (모든 패턴들마다 과사용을 주의하라는 말을 필수네요)

특히 동작이 절대 바뀔 일이 없다면 해당하는 뷰컨트롤러나 객체 컨텍스트 안에 직접 구현을 해도 괜찮습니다.

패턴의 핵심언제 동작들을 꺼낼지를 아는 것이고lazy 하게 어디에 필요할지 결정하면 그 때 수행해도 됩니다.

요약

  • 전략 패턴은 내부적으로 변경가능한(런타임 때 세팅 되거나 바뀌는) 객체들의 family 를 정의
  • 3가지 파트로 구성: 전략 프로토콜, 전략 구현부들, 전략을 사용하는 객체
  • delegate 패턴과 비교: 유연성을 위해 프로토콜을 사용하는 점에서 유사하나 전략패턴은 런타임때 전략이 바뀌는 반면 delegate는 보통 고정되어 있다는 점이 다름

--

--

J_sung_0o0
J_sung_0o0

Written by J_sung_0o0

Apple WWDC 19 & 20 Winner (The 1st two-time winner of all time from South Korea)

No responses yet