Although maybe not well known, class cluster is a widely used design pattern in the Cocoa framework. Class cluster uses a uniform and unique public class to define an interface, while replies on several private classes for detail implementation under the hood. The most advantage we could take from this is avoiding the complexity which multiple similar public classes could bring. A typical example of class cluster is NSNumber
: We have a series of methods to create an NSNumber
object from an integer, float or bool value. But in fact they belong to different private subclass:
NSNumber * num1 = [[NSNumber alloc] initWithInt:1];
// __NSCFNumber
NSNumber * num2 = [[NSNumber alloc] initWithFloat:1.0];
// __NSCFNumber
NSNumber * num3 = [[NSNumber alloc] initWithBool:YES];
// __NSCFBoolean
Class cluster is extremely useful when you have quite a few subclasses, but all of them follow a similar pattern. Your interface could be simplified so users of your code are able to ignore your implementation detail.
In Objective-C, the init
methods do very similar things with other normal methods. Since that, class clusters are implemented without any difficulty in Objective-C. You just replace the self
in init
method, and return proper subclass object based on the input type.
However, things become different in Swift. The init methods in Swift are real initiating methods. We can only get an object of that class when we call init, instead of getting an object of subclass. That means we cannot rely on the init method to construct a class cluster. To build class cluster in Swift, an effective way is using the factory pattern. The factory method of Drinking
below create a class cluster for two private class Coke
and Beer
:
class Drinking {
typealias LiquidColor = UIColor
var color: LiquidColor {
return LiquidColor.clearColor()
}
class func drinking(name: String) -> Drinking {
var drinking: Drinking
switch name {
case "Coke":
drinking = Coke()
case "Beer":
drinking = Beer()
default:
drinking = Drinking()
}
return drinking
}
}
class Coke: Drinking {
override var color: LiquidColor {
return LiquidColor.blackColor()
}
}
class Beer: Drinking {
override var color: LiquidColor {
return LiquidColor.yellowColor()
}
}
let coke = Drinking.drinking("Coke")
coke.color // Black
let beer = Drinking.drinking("Beer")
beer.color // Yellow
By using the trick mentioned in Get type of instance, we can confirm that coke
and beer
is an object of Coke
and Beer
respectively.
let cokeClass = NSStringFromClass(coke.dynamicType) //Coke
let beerClass = NSStringFromClass(beer.dynamicType) //Beer