iOS设计模式之类族(class cluster)

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

例如NSNumber类

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

cluster3.gif

如上图所示,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.使用类簇时要完善开发文档,避免团队成员错误使用。



posted @ 2016-07-22 16:59  俊华的博客  阅读(1141)  评论(0编辑  收藏  举报