Objective-C类族和工厂模式
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。
本文到这就整理这么多,更多内容可参看:
《Effective Objective-C》第9条
Objective-C类族和工厂模式的更多相关文章
- Java设计模式(三) 抽象工厂模式
原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...
- 深入浅出设计模式——抽象工厂模式(Abstract Factory)
模式动机在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法.但是有时候我们需要一个工厂可 ...
- C#设计模式(3):抽象工厂模式(Abstract Factory)(转载)
概述 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作:同时由于需求的变化,往往存在着更多系列对象的创建工作.如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来 ...
- .NET设计模式(3):抽象工厂模式(Abstract Factory)(转)
概述 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作:同时由于需求的变化,往往存在着更多系列对象的创建工作.如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来 ...
- Java设计模式系列之工厂模式
工厂模式将大量有共同接口的类实例化,工厂模式可以实现动态决定实例化哪一个类的对象,工厂模式在<Java与模式>中分为三类:1)简单工厂模式(Simple Factory):添加某一种类型的 ...
- .NET设计模式(3):抽象工厂模式(Abstract Factory)
):抽象工厂模式(Abstract Factory) 抽象工厂模式(Abstract Factory) --探索设计模式系列之三 Terrylee,2005年12月12日 转载:http://terr ...
- Java 抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是工厂方法模式的进一步抽象,其英文原话"Provide an interface for creating families ...
- 抽象工厂模式(Java与Kotlin版)
前文推送 设计模式 简单工厂模式(Java与Kotlin版) 工厂方法模式(Java与Kotlin版) Kotlin基础知识 Kotlin入门第一课:从对比Java开始 Kotlin入门第二课:集合操 ...
- java之设计模式工厂三兄弟之抽象工厂模式
[学习难度:★★★★☆,使用频率:★★★★★] 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工 ...
随机推荐
- Spark机器学习之推荐引擎
一. 最小二乘法建立模型 关于最小二乘法矩阵分解,我们可以参阅: 一.矩阵分解模型. 用户对物品的打分行为可以表示成一个评分矩阵A(m*n),表示m个用户对n各物品的打分情况.如下图所示: 其中,A( ...
- P1783 海滩防御
P1783 海滩防御 题目描述 WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和仓库总是被敌方派人偷袭 ...
- 跨域携带cookie
$.ajax({ type: "GET", url: "https://upload.cnblogs.com/imageuploader/upload?host=www. ...
- centos使用--软件启动关闭等操作的命令
关于centos服务的命令经常会遇到这几种 service systemctl /etc/init.d/ 1 service和/etc/init.d/ service是一个脚本命令,分析service ...
- Java学习笔记17---成员方法的重载与重写
重载是指,一个类中定义了一个成员方法后,通过修改参数个数.参数类型或参数顺序,重新实现该方法,则这两个方法互为对方的重载方法. 重写是指,子类重新实现父类的成员方法. 重载后的方法,与原方法相比: ( ...
- hbase shell出现ERROR:Can't get master address from Zookeeper;znode data==null
hbase shell出现ERROR:Can't get master address from Zookeeper;znode data==null(ERROR:org.apache.hadoop. ...
- 《Cracking the Coding Interview》——第11章:排序和搜索——题目4
2014-03-21 21:28 题目:给定一个20GB大小的文本文件,每一行都是一个字符串.请设计方法将这个文件里的字符串排序. 解法:请看下面的注释. 代码: // 11.4 Given a fi ...
- 《Cracking the Coding Interview》——第5章:位操作——题目3
2014-03-19 05:57 题目:给定一个整数N,求出比N大,而且二进制表示中和N有相同个数的‘1’的最小的数,比如3是‘11’,接下来的5是‘101’,再接下来的6是‘110’. 解法:从低位 ...
- Http状态码枚举(摘自 Microsoft 程序集 System.dll)
// 摘要: // 包含为 HTTP 定义的状态代码的值. public enum HttpStatusCode { // 摘要: // 等效于 HTTP 状态 100. System.Net.Htt ...
- 【java下午茶】12306的双人票
明天下午就要和客户谈需求了,今天还在列车上假象着明天的情景,由于这是一个旅游的项目,所以想尽可能设计得人性化一些. 不过有件很不爽的事情就是和老公的位子是分开的,虽然我们订的是连坐号.就这个问题也是我 ...