Words Of Life

Simple Factory Pattern

  • 摘要:利用ObjC实现简单工厂模式


前言

  • 最近重阅设计模式相关的书籍,阅读时无不感慨前人的智慧,希望以此文为头,记下与设计模式相关的笔记

目录

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

正文

简单工厂模式介绍

  • 基础理论

    简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现

    ​ —— 百度百科

  • 深入理解
    • 所谓简单工厂模式,本质就是根据传入的参数进行判断,动态选择不同的子类进行初始化。而为了方便对外使用,我们只暴露一个最最简单易用的父类 / 接口 / 工厂类。
    • 根据上述定义,我认为简单工厂模式有以下两种:
      • 形式I:抽象父类 + 具体子类(静态工厂方法定义在抽象父类中,该形式又被称作类簇模式,见UML图上半部分)
      • 形式II:工厂类 + 抽象父类 / 接口 + 具体子类(静态工厂方法定义在工厂类中,见UML图下半部分)
  • UML图
    • UML
  • 优与劣
    • 优:隐藏了实现类,用户在使用的时候无需知道对象如何构建的,对外仅暴露了必要的接口。
    • 劣:扩展时违反开闭原则(OCP,Open Closed Principle),当新增加子类的时候,需要修改在抽象父类 / 工厂类中的静态工厂方法。

简单工厂模式的使用实例

  • 下面以一个简单的二元加减乘除计算器为例,讲述简单工厂模式的应用

    • 创建父类:在本例中,父类为基础二元算术运算类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      // Operation.h
      #import <Foundation/Foundation.h>
      typedef NS_ENUM(NSUInteger, OperationType) {
      OperationTypeAdd,
      OperationTypeSub,
      OperationTypeMul,
      OperationTypeDiv,
      OperationTypeDefault
      };
      typedef NSInteger OperandType;
      @interface Operation : NSObject
      + (instancetype)operationWithType:(OperationType)type;
      - (OperandType)calculate;
      @property (nonatomic, assign, readwrite) OperandType operandA;
      @property (nonatomic, assign, readwrite) OperandType operandB;
      @end
    • 简单实现父类:本例采用形式I的简单工厂模式,故工厂方法放在父类中

      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
      // Operation.m
      #import "Operation.h"
      @implementation Operation
      + (instancetype)operationWithType:(OperationType)type {
      switch (type) {
      case OperationTypeAdd:
      return [[OperationAdd alloc] init];
      case OperationTypeSub:
      return [[OperationSub alloc] init];
      case OperationTypeMul:
      return [[OperationMul alloc] init];
      case OperationTypeDiv:
      return [[OperationDiv alloc] init];
      case OperationTypeDefault:
      return [[[self class] alloc] init];
      }
      }
      - (OperandType)calculate {
      // virtual implement
      NSAssert(NO, @"virtual implement shouldn`t be called by");
      return -1;
      }
      @end
    • 创建继承于父类的子类:本例中,子类为具体的操作类,负责实现操作运算。此处子类重写父类方法,以便合理使用多态

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // OperationAdd.h
      #import "Operation.h"
      @interface OperationAdd : Operation
      - (OperandType)calculate;
      @end
      // OperationAdd.m
      #import "OperationAdd.h"
      @implementation OperationAdd
      - (OperandType)calculate {
      return super.operandA + super.operandB;
      }
      @end
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // OperationSub.h
      #import "Operation.h"
      @interface OperationSub : Operation
      - (OperandType)calculate;
      @end
      #import "OperationSub.h"
      // OperationSub.m
      @implementation OperationSub
      - (OperandType)calculate {
      return super.operandA - super.operandB;
      }
      @end
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // OperationMul.h
      #import "Operation.h"
      @interface OperationMul : Operation
      - (OperandType)calculate;
      @end
      // OperationSub.m
      #import "OperationSub.h"
      @implementation OperationSub
      - (OperandType)calculate {
      return super.operandA - super.operandB;
      }
      @end
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // OperationDiv.h
      #import "Operation.h"
      @interface OperationDiv : Operation
      - (OperandType)calculate;
      @end
      // OperationDiv.m
      #import "OperationDiv.h"
      @implementation OperationDiv
      - (OperandType)calculate {
      if (super.operandB != 0) {
      return super.operandA / super.operandB;
      } else {
      return NSIntegerMax;
      }
      }
      @end
    • 实现简单工厂方法:实现父类中的简单工厂方法,根据类型判断返回的实例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // Operation.m
      #import "OperationAdd.h"
      #import "OperationSub.h"
      #import "OperationMul.h"
      #import "OperationDiv.h"
      + (instancetype)operationWithType:(OperationType)type {
      switch (type) {
      case OperationTypeAdd:
      return [[OperationAdd alloc] init];
      case OperationTypeSub:
      return [[OperationSub alloc] init];
      case OperationTypeMul:
      return [[OperationMul alloc] init];
      case OperationTypeDiv:
      return [[OperationDiv alloc] init];
      case OperationTypeDefault:
      return [[[self class] alloc] init];
      }
      }
    • 至此,简单工厂模式实现完毕

简单工厂模式在系统框架中的应用

  • NSString

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

      The objects you create using NSString and NSMutableString are referred to as string objects (or, when no confusion will result, merely as strings). The term C string refers to the standard char * type. Because of the nature of class clusters, string objects aren’t actual instances of the NSString or NSMutableString classes but of one of their private subclasses. Although a string object’s class is private, its interface is public, as declared by these abstract superclasses, NSString and NSMutableString. The string classes adopt the NSCopying and NSMutableCopying protocols, making it convenient to convert a string of one type to the other.

      ​ —— NSString API Reference

    • NSString类是一个类簇类。所谓类簇类,实际上是一个隐含着许多私有类的类。一般类簇类都带有工厂方法。我们查看NSString头文件,可以发现许多工厂方法(见下)。其本质亦是简单工厂模式,类簇类的私有类放在父类.m文件中。然后其利用传入的参数作为判断标准,生成私有类的实例,并作为NSString实例返回。

      1
      2
      3
      4
      5
      6
      + (instancetype)string;
      + (instancetype)stringWithString:(NSString *)string;
      + (instancetype)stringWithCharacters:(const unichar *)characters length:(NSUInteger)length;
      + (nullable instancetype)stringWithUTF8String:(const char *)nullTerminatedCString;
      + (instancetype)stringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
      + (instancetype)localizedStringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2);
    • 实际上,在系统框架中许多类都是以类簇的方式实现的,而以这种方式实现的类一般又含有类簇形式的简单工厂方法,因此类簇模式与简单工厂模式可谓是相得益彰。


  • 总结:本文简述了简单工厂模式的理论以及实践。我认为,简单工厂模式本质是根据传入的参数进行判断,动态选择不同的子类进行初始化。只要把握好这一本质,便能理清简单工厂模式。
  • 本文代码下载
  • 推荐阅读

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