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

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

Cocoa里的类族

  系统框架中有许多类族。大部分collection类都是类族,例如NSArray与其可变版本NSMutableArray。这样看来,实际上有两个抽象基类,一个用于不可变数组,另一个用于可变数组。尽管具备公共接口的类有两个,但仍然可以合起来算作一个类族。不可变的类定义了对所有数组都通用的方法,而可变的类则定义了那些只适用于可变数组的方法。两个类共属同一类族,这意味着二者在实现各自类型的数组时可以共用实现代码,此外,还能够把可变数组复制为不可变数组,反之亦然。
  在使用NSArray的alloc方法来获取实例时,该方法首先会分配一个属于某类的实例,此实例充当“占位数组”(placeholder array)。该数组稍后会转为另一个类的实例,而那个类则是NSArray的实体子类。这个过程稍显复杂,其完整的解释已经超出本书范围。
  像NSArray这样的类的背后其实是个类族(对于大部分collection类而言都是这样),明白这一点很重要,否则就可能会写出下面这种代码:

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

  你要是知道NSArray是个类族,那就会明白上述代码错在哪里:其中的if语句永远不可能为真。[maybeAnArray class]所返回的类绝不可能是NSArray类本身,因为由NSArray的初始化方法所返回的那个实例其类型是隐藏在类族公共接口(public facade)后面的某个内部类型(internal type)。
  不过,仍然有办法可以判断出某个实例所属的类是否位于类族之中。我们不用刚才那种写法,而是改用类型信息查询方法(introspection method)。本书第14条解释了这些方法的用法。若想判断某对象是否位于类族中,不要直接检测两个“类对象”是否等同,而应该采用下列代码:

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

  我们经常需要向类族中新增实体子类,不过这么做的时候得留心。在Employee这个例子中,若是没有“工厂方法”(factory method)的源代码,那就无法向其中新增雇员类别了。然而对于Cocoa中NSArray这样的类族来说,还是有办法新增子类的,但是需要遵守几条规则。这几条规则如下。
  子类应该继承自类族中的抽象基类。
  若要编写NSArray类族的子类,则需令其继承自不可变数组的基类或可变数组的基类。
  子类应该定义自己的数据存储方式。
  开发者编写NSArray子类时,经常在这个问题上受阻。子类必须用一个实例变量来存放数组中的对象。这似乎与大家预想的不同,我们以为NSArray自己肯定会保存那些对象,所以在子类中就无须再存一份了。但是大家要记住,NSArray本身只不过是包在其他隐藏对象外面的壳,它仅仅定义了所有数组都需具备的一些接口。对于这个自定义的数组子类来说,可以用NSArray来保存其实例。
  子类应当覆写超类文档中指明需要覆写的方法。
  在每个抽象基类中,都有一些子类必须覆写的方法。比如说,想要编写NSArray的子类,就需要实现count及“objectAtIndex:”方法。像lastObject这种方法则无须实现,因为基类可以根据前两个方法实现出这个方法。
  在类族中实现子类时所需遵循的规范一般都会定义于基类的文档之中,编码前应该先看看。

要点

  类族模式可以把实现细节隐藏在一套简单的公共接口后面。
  系统框架中经常使用类族。
  从类族的公共抽象基类中继承子类时要当心,若有开发文档,则应首先阅读。

  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