Words Of Life

Strategy Pattern

  • 摘要:利用ObjC实现策略模式


  • 目录

    • 策略模式的介绍
      • 基础理论
      • 深入理解
      • UML图
      • 优与劣
    • 策略模式的使用实例
    • 策略模式在系统框架中的应用

正文

策略模式介绍

  • 基础理论

    策略模式作为二十三种GoF软件设计模式的其中一种,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。

    ​ —— 维基百科

  • 深入理解

    • 所谓策略模式,本质就是将需要使用的算法抽象成一个持有的算法对象。在构造函数中选择具体的算法初始化持有的算法对象;在原本使用算法的地方,使用该算法对象调用算法。
    • 根据上述定义,我认为策略模式有以下形式:
      • 形式:策略上下文类 + 抽象父类 / 接口 + 具体子类
    • 如果单独使用策略模式,我们会需要在客户类创建具体的策略类对象,这时我们可以将策略模式与简单工厂模式结合使用,减少客户端的判断代码,两者配套食用,味道更佳!
  • UML图

    • UML
  • 优与劣

    • 优:以相同的形式调用所有的算法,降低算法类与使用算法的类(策略上下文类)的耦合度
    • 劣:扩展时违反开闭原则(OCP,Open Closed Principle),当新增加子类的时候,需要修改策略上下文类

策略模式的使用实例

  • 下面以一个简单的磁盘调度算法为例(算法实现仅用输出代替),讲述策略模式的应用

    • 创建父类:在本例中,父类为磁盘调度算法类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      // DiskSchedulingAlgorithm.h
      @interface DiskSchedulingAlgorithm : NSObject
      - (void)run;
      @end
      // DiskSchedulingAlgorithm.m
      #import "DiskSchedulingAlgorithm.h"
      @implementation DiskSchedulingAlgorithm
      - (void)run {
      NSAssert(NO, @"virtual implement shouldn`t be called by");
      }
      @end
    • 创建继承于父类的子类:本例中,子类为具体的磁盘调度算法类,负责实现具体算法。此处子类重写父类方法,以便合理使用多态

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // CSCANAlgorithm.h
      #import "DiskSchedulingAlgorithm.h"
      @interface CSCANAlgorithm : DiskSchedulingAlgorithm
      - (void)run;
      @end
      // CSCANAlgorithm.m
      #import "CSCANAlgorithm.h"
      @implementation CSCANAlgorithm
      - (void)run {
      NSLog(@"Do the CSCAN Algorithm");
      }
      @end
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // SCANAlgorithm.h
      #import "DiskSchedulingAlgorithm.h"
      @interface SCANAlgorithm : DiskSchedulingAlgorithm
      - (void)run;
      @end
      // SCANAlgorithm.m
      #import "SCANAlgorithm.h"
      @implementation SCANAlgorithm
      - (void)run {
      NSLog(@"Do the SCAN Algorithm");
      }
      @end
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // SSTFAlgorithm.h
      #import "DiskSchedulingAlgorithm.h"
      @interface SSTFAlgorithm : DiskSchedulingAlgorithm
      - (void)run;
      @end
      // SSTFAlgorithm.m
      #import "SSTFAlgorithm.h"
      @implementation SSTFAlgorithm
      - (void)run {
      NSLog(@"Do the SSTF Algorithm");
      }
      @end
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // FCFSAlgorithm.h
      #import "DiskSchedulingAlgorithm.h"
      @interface FCFSAlgorithm : DiskSchedulingAlgorithm
      - (void)run;
      @end
      // FCFSAlgorithm.m
      #import "FCFSAlgorithm.h"
      @implementation FCFSAlgorithm
      - (void)run {
      NSLog(@"Do the FCFS Algorithm");
      }
      @end
    • 实现策略上下文类:实现父类中的简单工厂方法,根据类型判断返回的实例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      // Context.h
      #import "DiskSchedulingAlgorithm.h"
      @class DiskSchedulingAlgorithm;
      @class FCFSAlgorithm;
      typedef NS_ENUM(NSUInteger, DiskSchedulingAlgorithmType) {
      kDiskSchedulingCSCANAlgorithmType,
      kDiskSchedulingSCANAlgorithmType,
      kDiskSchedulingFCFSAlgorithmType,
      kDiskSchedulingSSTFAlgorithmType,
      kDiskSchedulingDefaultAlgorithmType
      };
      typedef FCFSAlgorithm DefaultAlgorithmType;
      @interface Context : NSObject
      - (void)schedulingDisk;
      - (instancetype _Nullable)init;
      - (instancetype _Nullable)initWithDiskSchedulingAlgorithm:(DiskSchedulingAlgorithmType)type;
      @property (nonatomic, copy, readwrite, nonnull) DiskSchedulingAlgorithm *algorithm;
      @end
      // Context.m
      #import "Context.h"
      #import "CSCANAlgorithm.h"
      #import "SCANAlgorithm.h"
      #import "FCFSAlgorithm.h"
      #import "SSTFAlgorithm.h"
      @implementation Context
      - (instancetype)init {
      return [self initWithDiskSchedulingAlgorithm:kDiskSchedulingDefaultAlgorithmType];
      }
      // 简单工厂模式实现
      - (instancetype)initWithDiskSchedulingAlgorithm:(DiskSchedulingAlgorithmType)type {
      if (self = [super init]) {
      switch (type) {
      case kDiskSchedulingCSCANAlgorithmType:
      _algorithm = [[CSCANAlgorithm alloc] init];
      break;
      case kDiskSchedulingSCANAlgorithmType:
      _algorithm = [[SCANAlgorithm alloc] init];
      break;
      case kDiskSchedulingFCFSAlgorithmType:
      _algorithm = [[FCFSAlgorithm alloc] init];
      break;
      case kDiskSchedulingSSTFAlgorithmType:
      _algorithm = [[SSTFAlgorithm alloc] init];
      break;
      case kDiskSchedulingDefaultAlgorithmType:
      _algorithm = [[DefaultAlgorithmType alloc] init];
      break;
      }
      return self;
      }
      return nil;
      }
      - (void)schedulingDisk {
      [_algorithm run];
      }
      @end
    • 至此,策略模式实现完毕

策略模式在系统框架中的应用

  • 以下内容存有不确定性,倘若有误,劳烦告知
  • NSArray

    • 我们查阅NSArray API文档时会发现,有下面这一段话

      Remember that NSArray is the public interface for a class cluster and what this entails for your subclass. You must provide the storage for your subclass and implement the primitive methods that directly act on that storage.

      ​ —— NSArray API Reference

    • NSString类一样,NSArray亦是一个类簇类。对于类簇类而言,如果我们想要继承它,我们需要处理一系列较为复杂的问题。当我们需要使用NSArray的行为时(比如在NSArray之上实现一个栈结构),我们更好地方法是持有NSArray,而非继承。我们可以将NSArray当作我们其中一个策略,然后使用该策略去处理问题。


  • 总结:本文简述了策略模式的理论以及实践。我认为,策略模式本质是持有对象,并使用该对象去解决问题。我们应该去把握好这个本质。
  • 本文代码下载

  • Last Edited:2017.2.18
  • Author:@Seahub
  • Please contact me if you want to share this Article, 3Q~
五毛也是情, 一元也是爱