美文网首页
设计模式二:策略模式(Strategy Pattern)

设计模式二:策略模式(Strategy Pattern)

作者: _浅墨_ | 来源:发表于2019-01-11 18:33 被阅读9次
    一、原理理解

    在开发中,有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法。除此也可以将这些查找算法封装在一个统一的方法中,通过if…else…等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,则需要修改我们所封装的那个类的源代码。更换查找算法,也需要修改客户端调用代码。在这个自定义类中封装了大量查找算法,该类代码很复杂,维护起来很困难。

    为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法。在这里,每一个封装算法的类我们都可以称之为策略(Strategy)。为了保证这些策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应一个具体策略类。

    策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy Pattern)。

    策略模式包含如下角色:

    1. Context: 环境类
    2. Strategy: 抽象策略类
    3. ConcreteStrategy: 具体策略类

    如图所示:


    Strategy

    策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式,就是 “准备一组算法,并将每一个算法封装起来,使得它们可以互换”。

    在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。
    策略模式仅仅封装算法,提供将新算法插入到已有系统中,以及从系统中删除老算法。策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择在不同情景选择最合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。

    二、实践示例

    本例实现一个答题 demo。用户点击答题时候,可以选择是随机答题还是顺序答题。
    项目结构如下:


    1. 首先定义 QuestionStrategy 协议:
    public protocol QuestionStrategy: class {
    
      var title: String { get }
    
      var correctCount: Int { get }
      var incorrectCount: Int { get }
    
      func advanceToNextQuestion() -> Bool
    
      func currentQuestion() -> Question
    
      func markQuestionCorrect(_ question: Question)
      func markQuestionIncorrect(_ question: Question)
    
      func questionIndexTitle() -> String
    }
    
    1. 分别实现随机答题、顺序答题算法:

    RandomQuestionStrategy:

    import GameplayKit.GKRandomSource
    
    public class RandomQuestionStrategy: QuestionStrategy {
    
      // MARK: - Properties
      public var correctCount: Int = 0
      public var incorrectCount: Int = 0
      private let questionGroup: QuestionGroup
      private var questionIndex = 0
      private let questions: [Question]
    
      // MARK: - Object Lifecycle
      public init(questionGroup: QuestionGroup) {
        self.questionGroup = questionGroup
    
        let randomSource = GKRandomSource.sharedRandom()
        self.questions =
          randomSource.arrayByShufflingObjects(
            in: questionGroup.questions) as! [Question]
      }
    
      // MARK: - QuestionStrategy
      public var title: String {
        return questionGroup.title
      }
    
      public func currentQuestion() -> Question {
        return questions[questionIndex]
      }
    
      public func advanceToNextQuestion() -> Bool {
        guard questionIndex + 1 < questions.count else {
          return false
        }
        questionIndex += 1
        return true
      }
    
      public func markQuestionCorrect(_ question: Question) {
        correctCount += 1
      }
    
      public func markQuestionIncorrect(_ question: Question) {
        incorrectCount += 1
      }
    
      public func questionIndexTitle() -> String {
        return "\(questionIndex + 1)/\(questions.count)"
      }
    }
    

    SequentialQuestionStrategy:

    public class SequentialQuestionStrategy: QuestionStrategy {
      
      // MARK: - Properties
      public var correctCount: Int = 0
      public var incorrectCount: Int = 0
      private let questionGroup: QuestionGroup
      private var questionIndex = 0
      
      // MARK: - Object Lifecycle
      public init(questionGroup: QuestionGroup) {
        self.questionGroup = questionGroup
      }
      
      // MARK: - QuestionStrategy
      public var title: String {
        return questionGroup.title
      }
      
      public func currentQuestion() -> Question {
        return questionGroup.questions[questionIndex]
      }
      
      public func advanceToNextQuestion() -> Bool {
        guard questionIndex + 1 <
          questionGroup.questions.count else {
            return false
        }
        questionIndex += 1
        return true
      }
      
      public func markQuestionCorrect(_ question: Question) {
        correctCount += 1
      }
      
      public func markQuestionIncorrect(_ question: Question) {
        incorrectCount += 1
      }
      
      public func questionIndexTitle() -> String {
        return "\(questionIndex + 1)/" +
        "\(questionGroup.questions.count)"
      }
    }
    

    这两个策略同时遵循 QuestionStrategy ,只是对算法的实现不同,一个是随机生成题目,一个是顺序生成题目。

    1. 使用方法:

    当从 SelectQuestionGroupViewController 页面 push 到QuestionViewController 时候,把自己选好的 QuestionStrategy (或者顺序、或者随机)赋值给 QuestionViewController。

      public override func prepare(for segue: UIStoryboardSegue,
                                   sender: Any?) {
        guard let viewController = segue.destination
          as? QuestionViewController else { return }
        // 这里根据需要选择具体的实现策略
        viewController.questionStrategy = SequentialQuestionStrategy(
          questionGroup: selectedQuestionGroup)
        viewController.delegate = self
      }
    
    1. QuestionViewController 页面调用相应的方法:
     @IBAction func handleCorrect(_ sender: Any) {
        let question = questionStrategy.currentQuestion()
        questionStrategy.markQuestionCorrect(question)
    
        questionView.correctCountLabel.text =
          String(questionStrategy.correctCount)
        showNextQuestion()
      }
    
      @IBAction func handleIncorrect(_ sender: Any) {
        let question = questionStrategy.currentQuestion()
        questionStrategy.markQuestionIncorrect(question)
    
        questionView.incorrectCountLabel.text =
          String(questionStrategy.incorrectCount)
        showNextQuestion()
      }
    
    三、小结
    strategy pattern
    1. 什么时候使用策略模式?

    当有两个或多个可选择的不同行为时,可使用策略模式。

    策略模式类似于 delegation 模式:两种模式都依赖于协议而不是具体对象来提高灵活性。 因此,任何实现策略协议的对象都可以在运行时用作策略。

    与 delegation 不同,策略模式使用一系列对象。

    delegation 经常在运行时就已经确定了, 例如,可以从Interface Builder 设置 UITableView 的 dataSource 和delegate,并且在运行时很少更改它们。

    但是,策略旨在在运行时轻松互换。

    1. 注意:
    • 策略模式定义了一系列可在运行时设置或切换的可互换对象。
    • 此模式包含三个部分:使用策略的对象,策略协议和一系列策略对象。
    • 策略模式类似于委托模式:两种模式都使用协议来实现灵活性。 然而,与委托模式不同,策略意味着在运行时切换,而委托通常是固定不变的。

    demo 下载

    参考:

    1. Design Patterns in Swift: Strategy Pattern
    2. how-does-the-strategy-pattern-work
    3. 策略模式 strategy pattern

    相关文章

      网友评论

          本文标题:设计模式二:策略模式(Strategy Pattern)

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