# 9、以“类族模式”隐藏实现细节

​ “类簇”（class cluster）是一种很有用的模式（pattern），可以隐藏“抽象基类”（abstract base class）背后的实现细节。OC的系统框架中普遍使用此模式，大部分的`collection`类都是类簇，如：`UIButton`、`NSArray`、`NSMutableArray`等:

```objectivec
// 返回的对象，其类型取决于传入的按钮类型(button type)
// 不管返回什么类型的对象，都是继承自同一个基类：UIButton
// 意义：使用者无须关心属于哪个类，和其绘制等实现细节，只须知道如何创建，如何设置，如何使用就好
+ (UIButton *)buttonWithType:(UIButtonType)type; 

// 如果放在同一个类里，你可能会写出这样的代码：
- (void)drawRect:(CGRect)rect {
  if (_type == TypeA) {
    // Draw TypeA button
  } else if (_type == TypeB) {
    // Draw TypeB button
  }
  // 但是当类型和方法越来越多时，这样做救护很麻烦
}
```

​ 优秀的程序猿会重构为多个子类，把各个按钮所用的绘制方法放到相关子类中去。不过需要用户知道各种子类才行，此时应使用“类簇模式”：可以灵活应对多个类，讲他们的实现细节隐藏在抽象基类后面，以保持接口简洁。用户无须自己创建子类实例，只需调用基类方法来创建即可。

**创建类簇：**

```objectivec
// .h：声明枚举、属性、方法。。。
// .m：实现初始化方法：工程模式，是创建类簇的方式之一
+ (MOEmployee *)employeeWithType:(MOEmployeeType)type {
  swith (type) {
    case MOEmployeeTypeDeveloper: return [MOEmployeeDeveloper new];
    case MOEmployeeTypeDesigner: return [MOEmployeeDesigner new];
    case MOEmployeeTypeFinance: return [MOEmployeeFinance new];
  }
}
- (void)doWork {
  // 子类们实现各自的工作内容
}
// 子类继承MOEmployee，并实现doWork方法。。。
// 另外注意：
[anEmployee isMemberOfClass:[MOEmployee class]] // 永远为NO!!!
```

**Cocoa里的类簇：**

​ 你要是知道`NSArray`是个类簇，就不会写出下面第一行这样的代码，`[anArray class]`所返回的类绝对不可能是`NSArray`类本身！！！

```objectivec
if ([anArray class] == [NSArray class]) {} // 永远为NO!!!
// Array的初始化方法返回的是隐藏的某个内部类型

// 不过可以这样判断是都属于其类簇中
if ([anArray isKindOfClass:[NSArray class]]) {} // YES
```

​ 想为类簇新增子类，需要遵循几条规则：

* 子类应该继承自类簇中的抽象基类（如：不可变数组的基类 or 可变数组的基类）
* 子类应该定义自己的数据存储方式（如：Array子类，可以用Array来存储，Array不会自动保存数据）
* 子类应当覆写超类文档中指明需要覆写的方法（如：`Array`的`count`、`objectAtIndex:`）       &#x20;

要点：

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