首页 > ios > 第9条:以“类族模式”隐藏实现细节(1)

第9条:以“类族模式”隐藏实现细节(1)

  “类族”(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)这样的属性,如何增加触摸动作的目标对象等问题就好。
  回到开头说的那个问题上,我们可以把各种按钮的绘制逻辑都放在一个类里,并根据按钮类型来切换:

- (void)drawRect:(CGRect)rect {  
    if (_type == TypeA) {  
        // Draw TypeA button  
    } else if (_type == TypeB) {  
        // Draw TypeB button  
    } /* ... */  
} 

  这样写现在看上去还算简单,然而,若是需要依按钮类型来切换的绘制方法有许多种,那么就会变得很麻烦了。优秀的程序员会将这种代码重构为多个子类,把各种按钮所用的绘制方法放到相关子类中去。不过,这么做需要用户知道各种子类才行。此时应该使用“类族模式”,该模式可以灵活应对多个类,将它们的实现细节隐藏在抽象基类后面,以保持接口简洁。用户无须自己创建子类实例,只需调用基类方法来创建即可。

创建类族

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

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的成员方法,这暗示该类的实例也许不应该由用户直接创建。还有一种办法可以确保用户不会使用基类实例,那就是在基类的doADaysWork方法中抛出异常。然而这种做法相当极端,很少有人用。
  如果对象所属的类位于某个类族中,那么在查询其类型信息(introspection)时就要当心了(参见第14条)。你可能觉得自己创建了某个类的实例,然而实际上创建的却是其子类的实例。在Employee这个例子中,[employee isMemberOfClass:[EOCEmployee class]]似乎会返回YES,但实际上返回的却是NO,因为employee并非Employee类的实例,而是其某个子类的实例。

  1. 还没有评论
评论提交中, 请稍候...

留言


可以使用的标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Trackbacks & Pingbacks ( 0 )
  1. 还没有 trackbacks