类族模式在UIKit(user interface framework)使用的范围已经远远超过我们的想象,比如,UIButton,NSArray,NSString,NSNumber等,

例如NSNumber类

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

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

“Simple Concept and Simple Interface”,这正是苹果设计类族的初衷,也是类族的优点所在。可以想象我们要用整数作为参数拿到一个NSNumber对象和一个布尔值参数拿到的NSNumber对象是不同的,这略微有些类似于switch逻辑(虽然是通过不同的方法),根据不同的条件提供不同的子类对象,而这一切都集中声明在公共接口类NSNumber中。通过[NSNumber numberWithInt:2]和[NSNumber numberWithBool:YES]得到的对象对应的类,一个是__NSCFNumber,另一个是__NSCFBoolean。

例如创建按钮时调用的

+ (instancetype)buttonWithType:(UIButtonType)buttonType;

这个方法创建的对象的类型取决于传入的按钮类型buttonType。返回的对象都继承于同一个基类UIButton
当各个子类在外部表现相同,内部实现不同时,我们就可以使用类簇模式去实现它。这样做我们无需使用对应的子类,只需要使用基类即可。

首先我们先来看这两个函数:

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;

第一个函数的意思是:接收者是否是aClass类的实例或者从这个类继承的任何类的实例。如果是返回yes。
第二个函数的意思是:接收者是否是aClass的实例,如果是返回yes。
按照上面的解释我们执行下面两行代码输出结果应该是:isKindOfClass
,isMemberOfClass

 NSArray*array = [NSArray new];
if ([array isKindOfClass:[NSArray class]]) {
NSLog(@"isKindOfClass");
}
if ([array isMemberOfClass:[NSArray class]]) {
NSLog(@"isMemberOfClass");
}

但是上面代码执行的输出结果却是:isKindOfClass。
为什么会出现这种情况?isMemberOfClass为什么没有执行?array明明是NSArray的一个实例呀!难道不是吗?为了验证我们的怀疑我们在执行下面一段代码看看array到底是什么

NSLog(@"%@",[[array class] debugDescription]);
NSLog(@"%@", [[[NSArray arrayWithObject:@"a,b"] class] debugDescription]);

输出:NSArray0,NSArrayI
好吧,array果然不是NSArray的一个实例,但是上面输出了“isKindOfClass,这说明array是NSArray子类的一个实例,到底这种推断正不正确?我们再来验证一下:

NSLog(@"%@",[[array superclass] debugDescription]);

输出:NSArray
好吧,果然是这样,写到这儿我们已经大致明白了,通过不同方法实例化的array都是NSArray子类的实例,NSArray是一个抽象的基类。这种模式就是类族模式。

但是如果把NSArray换成UIButton又回出现截然相反的情况,出现这种情况的原因在于NSArray基类又对子类进行了重构,生成了多个子类。

举个例子,我们要写个刷新控件,实现下拉刷新,上拉加载功能。
定义抽象基类

typedef enum {
RefreshStateBeginRefreshing,//开始刷新
RefreshStateEndRefreshing//结束刷新 } RefreshState; /**
控件类型
*/
typedef enum {
RefreshHeaderType,
RefreshFooterType
} RefreshViewType; @interface XZHRefreshView : UIView - (void)addRefreshTarget:(id)target action:(SEL)action forRefreshState:(RefreshState)state; + (XZHRefreshView *)refreshViewWithType:(RefreshViewType)type; @end @implementation XZHRefreshView + (XZHRefreshView *)refreshViewWithType:(RefreshViewType)type {
if (type == RefreshHeaderType) {
return [XZHRefreshHeaderView header];
} else if (type == RefreshFooterType) {
return [XZHRefreshFooterView footer];
} else {
return nil;
}
}
- (void)addRefreshTarget:(id)target action:(SEL)action forRefreshState:(RefreshState)state {
//子类实现
}
@end

每个实体子类,都是继承于基类:

@interface XZHRefreshHeaderView : XZHRefreshView

@end

@implementation XZHRefreshView

- (void)addRefreshTarget:(id)target action:(SEL)action forRefreshState:(RefreshState)state {
//code
}
@end

举个例子我们自己写一个类族,定义一个继承与NSObject的抽象基类LULEmployee,基类中定义一个枚举类型

LULEmployee.h 代码

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSUInteger,LULEmployeeType) {

WJUEmployeeTypeDevlopers,

WJUEmployeeTypeProducters,

};

@interface WJUEmployee : NSObject

+(WJUEmployee*)employeeWithType:  (WJUEmployeeType)type;

-(void)doADayWork;

@end

WJUEmployee.m代码

#import "WJUEmployee.h"

#import "WJUEmployeeTypeDevloper.h"

#import "WJUEmployeeTypeProducter.h"

@implementation WJUEmployee

+(WJUEmployee*)employeeWithType:  (WJUEmployeeType)type{

switch (type) {

case 0:

return [WJUEmployeeTypeDevloper new];

break;

case 1:

return [WJUEmployeeTypeProducter new];

break;

default:

break;

}

}

-(void)doADayWork{

}

@end

WJUEmployeeTypeDevloper.h代码

#import "WJUEmployee.h"

@interface WJUEmployeeTypeDevloper : WJUEmployee

@end

WJUEmployeeTypeDevloper.m代码

#import "WJUEmployeeTypeDevloper.h"

@implementation WJUEmployeeTypeDevloper

-(void)doADayWork{

[super doADayWork];

NSLog(@"%@",[[self class] description]);

}

@end

同理WJUEmployeeTypeProducter一样

然后创建3个继承与基类的子类,并在子类中覆写-(void)doADayWork方法;

类族模式最大的好处:

  • 可以隐藏抽象基类背后的复杂细节,
  • 程序员不需要记住各种类型的创建对象所需要的具体类,简化开发人员开发成本,提高开发效率.
  • 使用者只需调用基类简单的方法就可以返回不同的子类实例。

类族缺点

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

注意事项

1.所有子类应该继承类簇的抽象基类
2.子类应该覆写父类需要覆写的方法。
3.使用类簇时要完善开发文档,避免团队成员错误使用。

iOS设计模式之类族(class cluster)的更多相关文章

  1. iOS设计模式之生成器

    iOS设计模式之生成器 1.生成器模式的定义 (1): 将一个复杂的对象的构件与它的表示分离,使得相同的构建过程能够创建不同的表示 (2): 生成器模式除了客户之外还包括一个Director(指导者) ...

  2. IOS设计模式之一(MVC模式,单例模式)

    iOS 设计模式-你可能已经听说过这个词,但是你真正理解它意味着什么吗?虽然大多数的开发者可能都会认为设计模式是非常重要的,然而关于设计模式这一主题的文章却不多,并且有时候我们开发者在写代码的时候也不 ...

  3. iOS 设计模式之工厂模式

    iOS 设计模式之工厂模式 分类: 设计模式2014-02-10 18:05 11020人阅读 评论(2) 收藏 举报 ios设计模式 工厂模式我的理解是:他就是为了创建对象的 创建对象的时候,我们一 ...

  4. IOS设计模式之三:MVC模式

    IOS设计模式之三:MVC模式   模型-视图-控制器 这个模式其实应该叫做MCV,用控制器把model与view隔开才对,也就是model与view互相不知道对方的存在,没有任何瓜葛,他们就像一个团 ...

  5. iOS设计模式 - 享元

    iOS设计模式 - 享元 原理图 说明 享元模式使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件.通常物件中的部分 ...

  6. iOS设计模式 - 责任链

    iOS设计模式 - 责任链 原理图 说明 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...

  7. iOS设计模式 - 模板

    iOS设计模式 - 模板 原理图 说明 定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. 源码 https://github.c ...

  8. iOS设计模式 - 访问者

    iOS设计模式 - 访问者 原理图 说明 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 1.Visitor 抽象访问者角色,为该对象结构中具 ...

  9. iOS设计模式 - 迭代器

    iOS设计模式 - 迭代器 原理图 说明 提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示. 源码 https://github.com/YouXianMing/iOS-Des ...

随机推荐

  1. enter快捷键盘

    protected override bool ProcessDialogKey(Keys keyData) { #region PageDown if (keyData == Keys.Enter) ...

  2. 【C#/WPF】用System.Timers.Timer计时器做浮窗广告

    需求:鼠标静止一段时间后,显示浮窗广告. 思路:界面XAML写好一个专门显示浮窗广告的Canvas,先设为不可见Visibility=”Collapsed”,然后用System.Timers.Time ...

  3. Linux远程复制文件

    将本机文件app.properties 复制到用户为root,ip为ip的具体路径下去 scp app.properties root@ip:/apps/javaconf/common/ 其他参考: ...

  4. lua中table如何安全移除元素

    在Lua中,table如何安全的移除元素这点挺重要,因为如果不小心,会没有正确的移除,造成内存泄漏. 引子 比如有些朋友常常这么做,大家看有啥问题 将test表中的偶数移除掉local test = ...

  5. KBEngine.executeRawDatabaseCommand使用

    先贴一段官方的API介绍: def executeRawDatabaseCommand( command, callback, threadID, dbInterfaceName ): 功能说明: 这 ...

  6. PCL,VTK及其依赖库的编译-十分详细

    所有库的编译教程都很详细,全都上传到百度文库. 1.VS2013-Qt5.5.1-动态编译-VTK7.0.0http://wenku.baidu.com/view/749528a433687e21ae ...

  7. MYSQL的用户变量(@)和系统变量(@@)

    9.3. 用户变量 可以先在用户变量中保存值然后在以后引用它:这样可以将值从一个语句传递到另一个语句.用户变量与连接有关.也就是说,一个客户端定义的变量不能被其它客户端看到或使用.当客户端退出时,该客 ...

  8. 后序线索二叉树中查找结点*p的后继

    在后序线索二叉树中查找结点*p的后继: 1.若结点*p为根,则无后继:2.若结点*p为其双亲的右孩子,则其后继为其双亲:3.若结点*p为其双亲的左孩子,且双亲无右子女,则其后继为其双亲:4.若结点*p ...

  9. android2.2应用开发之IccCard(sim卡或USIM卡)(转至 http://www.2cto.com/kf/201306/223784.html)

    如果要做android通讯录的联系人的机卡混排显示,由于手机卡类型的不同,导致手机卡存储容量以及可以存储信息不同,就要涉及到android去读Icc卡的信息. 一般的sim卡只能存储姓名跟一个电话号码 ...

  10. 弹窗插件zDialog使用教程

    1.首先现在好zDialog然后复制项目中 2.配置zDialog解压以后images文件夹位置 images存放位置根据自己实际项目而定,zDialog.js中配置位置即可,如: var IMAGE ...