类的抽象和继承:类族与工厂模式

“类族”(class cluster)是一种很有用的模式(pattern),可以隐藏“抽象基类”(abstract base class)背后的实现细节。Objective-C的系统框架中普遍使用此模式,比如iOS的用户界面框架(user interface framework)UIKit中就有一个名为UIButton的类,想创建按钮,需要调用下面这个“类方法”(class method):

+ (UIButton*)buttonWithType:(UIButtonType)type; 

该方法所返回的对象,其类型取决于传入的按钮类型(button type),然而,不管返回什么类型的对象,它们都继承自同一个基类:UIButton。这么做的意义在于:UIButton类的使用者无须关心创建出来的按钮具体属于哪个子类,也不用考虑按钮的绘制方式等实现细节,使用者只需明白如何创建按钮,如何设置像“标题”(title)这样的属性,如何增加触摸动作的目标对象等问题就好。

 

创建类族

现在举例来演示如何创建类族,假设有一个处理雇员的类,每个雇员都有“名字”和“薪水”这两个属性,管理者可以命令其执行日常工作,但是,各种雇员的工作内容却不同,经理在带领雇员做项目时,无须关心每个人如何完成其工作,仅需指示其开工即可。

首先要定义抽象基类:

typedef NS_ENUM(NSUInteger, EOCEmployeeType) {  
    EOCEmployeeTypeDeveloper,  
    EOCEmployeeTypeDesigner,  
    EOCEmployeeTypeFinance,  
};  
 
@interface EOCEmployee : NSObject  
 
@property (copy) NSString *name;  
@property NSUInteger salary;  
 
// Helper for creating Employee objects  
+ (EOCEmployee*)employeeWithType:(EOCEmployeeType)type;  
 
// Make Employees do their respective day's work  
- (void)doADaysWork;  
 
@end  
 
@implementation EOCEmployee  
 
+ (EOCEmployee*)employeeWithType:(EOCEmployeeType)type {  
    switch (type) {  
        case EOCEmployeeTypeDeveloper:  
            return [EOCEmployeeDeveloper new];  
            break;  
        case EOCEmployeeTypeDesigner:  
            return [EOCEmployeeDesigner new];  
            break;  
        case EOCEmployeeTypeFinance:  
            return [EOCEmployeeFinance new];  
            break;  
    }  
}  
 
- (void)doADaysWork {  
    // Subclasses implement this.  
}  
 
@end 

每个“实体子类”(concrete subclass)都从基类继承而来,例如:

@interface EOCEmployeeDeveloper : EOCEmployee  
@end  
 
@implementation EOCEmployeeDeveloper  
 
- (void)doADaysWork {  
    [self writeCode];  
}  
 
@end 

在本例中,基类实现了一个“类方法”,该方法根据待创建的雇员类别分配好对应的雇员类实例,这种“工厂模式”(Factory pattern)是创建类族的办法之一。

可惜Objective-C这门语言没办法指明某个基类是“抽象的”(abstract),于是,开发者通常会在文档中写明类的用法,这种情况下,基类接口一般都没有名为init的成员方法,这暗示该类的实例也许不应该由用户直接创建。

 

Cocoa里的类族

系统框架中有许多类族,大部分collection类都是类族,如NSArray,下面这种代码:

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

其中的if语句永远不可能为真。[maybeAnArray class]所返回的类绝不可能是NSArray类本身,因为由NSArray的初始化方法所返回的那个实例其类型是隐藏在类族公共接口(public facade)后面的某个内部类型(internal type)。

要判断出某个实例所属的类是否位于类族之中,应该使用类型信息查询方法(introspection method),不要直接检测两个“类对象”是否等同,而应该采用下列代码:

id maybeAnArray = /* ... */;  
if ([maybeAnArray isKindOfClass:[NSArray class]]) {  
        // Will be hit  
} 
posted @ 2016-04-19 22:25  Gabriel_Lee  阅读(975)  评论(0编辑  收藏  举报