http://www.cocoachina.com/ios/20141124/10296.html

相信大家都了解GoF的《Design Patterns》中提到的23种设计模式,其中将常见的设计模式分为三大类:创建型模式、行为型模式、结构型模式。而在《Clean Code》中也提到建造酒店的例子,系统中对象的构建和使用应当分离开,那么应该怎么构建对象更加整洁和符合使用场景就很重要。

在iOS的系统类库中也有一种方式使得开发者不必关注类中具体的存储实现,但可以根据不同需求场景创建出合适的对象来。比如Foudation中的NSArray、UIkit中的UIButton。

本文结合几种工厂设计模式的原理,对Objective-C类族的概念做一下简要的整理。

0. 三种工厂

其实除了《Design Patterns》中提到的Factory Method和Abstract Factory,常提到的还有另一种工厂模式Simple Factory。

在简单工厂中,产品有一个统一的interface,而可以有不同implementation,同时有一个(通常是仅有一个)工厂对象。需要产品的时候,工厂会根据已有条件做switch,选择一种产品实现,构建一个实例出来。这种设计将产品的抽象和实现分离开来,可以针对统一的产品接口进行通信,而不必特意关注具体实现的不同。对于产品类别的扩充也是可以的,但每增加一个产品,都需要修改工厂的逻辑,有一定维护成本。

工厂方法更进一步,将工厂也抽象出来,进行接口、实现分离。这样具体工厂和具体产品可以对应着同时扩充,而不需要修改现有逻辑。当然,使用者也许在不同场景要在一定程度上自己对应的工厂选择(这个总要有人知道,不可避免)。

抽象工厂相对于工厂方法主要是对整个产品类的体系进行了横向扩充,构成一个更为完整的系统。

1. Objective-C的类族(Class Cluster)

做iOS开发的朋友们一定用过NSNumber的numberWith…方法。但大家有可能都不知道NSNumber这样的方法调用返回的不是NSNumber类本身的对象,这正是Objective-C类族的微妙之处。

如上图所示,Number的概念很大。而实际上NSNumber实际上是有很多隐藏的子类的,而我们通过NSNumber的numberWith…方法得到的对象正是其子类的对象,但对于使用者几乎可以不必知道这一点,只要知道它是一个NSNumber对象就OK了。

“Simple Concept and Simple Interface”,这正是苹果设计类族的初衷,也是类族的优点所在。可以想象我们要用整数作为参数拿到一个NSNumber对象和一个布尔值参数拿到的NSNumber对象是不同的,这略微有些类似于switch逻辑(虽然是通过不同的方法),根据不同的条件提供不同的子类对象,而这一切都集中声明在公共接口类NSNumber中。我们很容易联想到上面提到的Simple Factory(简单工厂)设计模式。

没错,与简单工厂类似,类族的一个缺点也显现出来,那就是已有的类族不好扩展。比如你想NSNumber再多支持一种情况,这个恐怕很难。好在这些系统的类库已经将大部分可能都做进去了,考虑得比较完善,通常你只是去用就可以了。

2. 类族的子类扩展

了解了类族的概念,我们在实际开发当中也可以采用其方式,利用其优点。上面提到对已有类族进行子类扩展是很难的,但这不代表NSNumber、NSArray等类就没法继承了。他们还是可以有自定义的子类的。

既然要做类族的子类,就要做到:

· 以公共“抽象”类为父类,比如NSNumber、NSArray等,而非其子类

· 提供自定义存储

· 重写(覆盖)父类所有初始化方法

· 重写父类中“原始”方法

其中第二点最重要,系统的类族通常在父类中只是提供了各种方法声明,而自身并不提供存储,所以要自定义子类一定要提供自己的存储,一般情况下这也是自定义子类的意义所在。

重写初始化方法,要遵从Objective-C初始化链的规范。

而重写“原始”方法,这个要说一下。按照苹果的文档,和指定初始化方法形式类似,这些类里面的众多方法可以分为两类,“原始”方法和“衍生”方法。“原始”方法定义了这个类及对象的最基本行为,而“衍生”方法则基于这些“原始”方法进行更复杂逻辑的包装。所以,重写了“原始”方法,“衍生”方法也自然效果就不同了。

除了自定义子类外,苹果官方更建议开发者用组合的方式对类族类进行包装。

3. 对象所属类的判断

有人会问,如果我没有特殊需求,不需要写NSArray、NSNumber的子类,是不是了解类族就没有多大意义了。这里记一下,通过了解类族概念,我们至少知道了,通过NSNumber得到的对象,不一定是(基本上就不会是)NSNumber类本身的对象。

可以试验下,通过[NSNumber numberWithInt:2]和[NSNumber numberWithBool:YES]得到的对象对应的类,一个是__NSCFNumber,另一个是__NSCFBoolean。 那么,如下这样的判断就不行了:

1
2
3
4
id maybeAnArray = /* ... */;
if ([maybeAnArray class] == [NSArray class]) {
   // Will never be hit
}

需要在适当的情况下选择使用isMemberOfClass和isKindOfClass。

本文到这就整理这么多,更多内容可参看:

ClassClusters

《Effective Objective-C》第9条

Objective-C类族和工厂模式的更多相关文章

  1. Java设计模式(三) 抽象工厂模式

    原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...

  2. 深入浅出设计模式——抽象工厂模式(Abstract Factory)

    模式动机在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法.但是有时候我们需要一个工厂可 ...

  3. C#设计模式(3):抽象工厂模式(Abstract Factory)(转载)

    概述 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作:同时由于需求的变化,往往存在着更多系列对象的创建工作.如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来 ...

  4. .NET设计模式(3):抽象工厂模式(Abstract Factory)(转)

    概述 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作:同时由于需求的变化,往往存在着更多系列对象的创建工作.如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来 ...

  5. Java设计模式系列之工厂模式

    工厂模式将大量有共同接口的类实例化,工厂模式可以实现动态决定实例化哪一个类的对象,工厂模式在<Java与模式>中分为三类:1)简单工厂模式(Simple Factory):添加某一种类型的 ...

  6. .NET设计模式(3):抽象工厂模式(Abstract Factory)

    ):抽象工厂模式(Abstract Factory) 抽象工厂模式(Abstract Factory) --探索设计模式系列之三 Terrylee,2005年12月12日 转载:http://terr ...

  7. Java 抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern)是工厂方法模式的进一步抽象,其英文原话"Provide an interface for creating families ...

  8. 抽象工厂模式(Java与Kotlin版)

    前文推送 设计模式 简单工厂模式(Java与Kotlin版) 工厂方法模式(Java与Kotlin版) Kotlin基础知识 Kotlin入门第一课:从对比Java开始 Kotlin入门第二课:集合操 ...

  9. java之设计模式工厂三兄弟之抽象工厂模式

    [学习难度:★★★★☆,使用频率:★★★★★]  工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工 ...

随机推荐

  1. [Bzoj4408]神秘数(主席树)

    Description 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数. 例如S={1,1,1,4,13}, 1 = 1 2 = 1+1 3 = 1+1+1 4 = 4 5 = ...

  2. Snowflake Snow Snowflakes【Poj3349】

    Description You may have heard that no two snowflakes are alike. Your task is to write a program to ...

  3. SVN 的基本用法

    克隆远程库 # svn checkout $URL --username=$userName 显示库信息 # svn info 显示库状态 # svn status 将文件纳入版本管理 # svn a ...

  4. idea中用maven打包spring的java项目(非web)

    之前一直用安装的maven打包spring的javaweb项目,用的是mvn assembly:assembly打包,这次打包非web的spring项目,遇到许多问题,特记录一下正确步骤. 1.配置p ...

  5. format内置函数

    1. 函数功能将一个数值进行格式化显示. 2. 如果参数format_spec未提供,则和调用str(value)效果相同,转换成字符串格式化. >>> format(3.14159 ...

  6. WPF系列教程——(二)使用Prism实现MVVM设计模式 - 简书

    原文:WPF系列教程--(二)使用Prism实现MVVM设计模式 - 简书 本文假设你已经知道MVVM设计模式是什么,所以直接进入正题,今天我们就用Prism来实现WPF的MVVM设计模式,百度上关于 ...

  7. layout焊盘过孔大小的设计标准

    PCB设计前准备 1.准确无误的原理图.包括完整的原理图文件和网表,带有元件编码的正式的BOM.原理图中所有器件的PCB封装(对于封装库中没有的元件,硬件工程师应提供datasheet或者实物,并指定 ...

  8. 手把手教你如何逐步安装OpenStack

    [TechTarget中国原创] 尽管OpenStack官方提供的在线安装教程和分步向导能够为管理员提供很大帮助,但是依然存在很多不尽如人意的地方.因此在Ubuntu上安装OpenStack的过程当中 ...

  9. USACO Section2.3 Zero Sum 解题报告 【icedream61】

    zerosum解题报告----------------------------------------------------------------------------------------- ...

  10. paramiko类Fabric主机管理

    环境:Linux python3.5 要求:类 Fabric 主机管理程序开发:1. 运行程序列出主机组或者主机列表2. 选择指定主机或主机组3. 选择让主机或者主机组执行命令或者向其传输文件(上传/ ...