美文网首页
读《Design Patterns by Tutorials》笔

读《Design Patterns by Tutorials》笔

作者: Beyond_JG | 来源:发表于2018-10-03 11:28 被阅读18次

    策略模式定义了一系列可交换的对象,这些对象可以在运行时设置或者切换。这个模式有三部分。

    • 使用策略的对象:在 iOS 开发中这个对象通常是一个UIViewController,但是理论上可以是任何需要交换表现的对象。
    • 策略协议:定义了每个策略必须实现的方法。
    • 策略对象:遵守策略协议的对象。

    UML 图如下:


    策略模式

    When should you use it?

    当你有两个或多个需要交换的表现时需要使用策略模式。
    这个模式和委托模式相似:因为两个模式都是依赖于协议而不出具体对象来提高弹性。通常,任何实现了策略协议的对象可以在运行时被用做协议对象。
    不想委托,策略模式使用一系列对象。
    委托经常在运行时确定。比如,UITableViewdataSourcedelegate可以在 Interface Builder 中设置,但是它们在运行时很少改变。
    然而,策略的目的是在运行时可以轻松的交换。

    Playground example

    策略模式是一种表现型模式,因为策略模式是关于一个对象使用另一个对象来做一些事情。
    下面的代码例子是一个关于电影评分的例子。想象一个 app 使用几个电影评分服务。比如:烂番茄,IMDb和Metacritic。为了不重复为每种服务写代码,我们来使用策略模式来简化实现。可以创建一个定义了通用 API 的协议来获取每种服务。
    首先我们定义一个策略协议:

    public protocol MovieRatingStrategy {
      // 1
      var ratingServiceName: String { get }
      
      // 2
      func fetchRating(for movieTitle: String,
               success: (_ rating: String, _ review: String) -> ())
    }
    
    1. 我们使用ratingServiceName来表示提供评分的服务。比如:它可能是烂番茄。
    2. 我们使用fetchRatingForMovieTitle(_:success:)来获取异步获取电影评分。在真实的 app 中,可能需要一个失败的 closure。

    接下来,添加烂番茄客户端的实现代码:

    public class RottenTomatoesClient: MovieRatingStrategy {
      public let ratingServiceName = "Rotten Tomatoes"
      
      public func fetchRating(
        for movieTitle: String,
        success: (_ rating: String, _ review: String) -> ()) {
        
        // In a real service, you'd make a network request...
        // Here, we just provide dummy values...
        let rating = "95%"
        let review = "It rocked!"
        success(rating, review)
      }
    }
    
    

    然后,添加 IMDb 客户端的实现代码:

    public class IMDbClient: MovieRatingStrategy {
      public let ratingServiceName = "IMDb"
      
      public func fetchRating(
        for movieTitle: String,
        success: (_ rating: String, _ review: String) -> ()) {
        
        let rating = "3 / 10"
        let review = """
        It was terrible! The audience was throwing rotten
        tomatoes!
        """
        success(rating, review)
      }
    }
    

    因为所有提供服务的客户端都遵守了MovieRatingStrategy。使用策略的对象不必要直接指导它们的类型,代替的,它们仅仅依赖协议。
    作为例子,添加下面代码:

    import UIKit
    
    public class MoviewRatingViewController: UIViewController {
      
      // MARK: - Properties
      public var movieRatingClient: MovieRatingStrategy!
      
      // MARK: - Outlets
      @IBOutlet public var movieTitleTextField: UITextField!
      @IBOutlet public var ratingServiceNameLabel: UILabel!
      @IBOutlet public var ratingLabel: UILabel!
      @IBOutlet public var reviewLabel: UILabel!
      
      // MARK: - View Lifecycle
      public override func viewDidLoad() {
        super.viewDidLoad()
        ratingServiceNameLabel.text =
          movieRatingClient.ratingServiceName
      }
      
      // MARK: - Actions
      @IBAction public func searchButtonPressed(sender: Any) {
        guard let movieTitle = movieTitleTextField.text
          else { return }
        
        movieRatingClient.fetchRating(for: movieTitle) {
          (rating, review) in
          self.ratingLabel.text = rating
          self.reviewLabel.text = review
        }
      }
    }
    
    

    当这个 view controller 在 app 中实例化时,需要设置movieRatingClient属性。注意,view controller 不知道MovieRatingStrategy具体实现。
    使用哪个 MovieRatingStrategy 的决定可以推迟到运行时,可以让用户选择。

    What should you be careful about?

    小心滥用这个模式。实际情况下,如果一个表现不会改变,可以直接放到使用的view controller 或者对象的上下文中。使用这个模式得技巧是知道什么时候切换行为。并且可以在确定需要的地方 lazy 的去做。

    Tutorial project

    我们接着委托模式继续做那个 app。我们添加一个可以随机顺序回答问题的机制。这样我们就可以不按顺序回答问题了。但是,有些人可能想要顺序回答问题。这里我们用策略模式来实现。实现效果:

    策略示例

    Demo

    预告

    下一节将要介绍单例模式。

    相关文章

      网友评论

          本文标题:读《Design Patterns by Tutorials》笔

          本文链接:https://www.haomeiwen.com/subject/uhuzoftx.html