Now that we have all of the components required to implement our public wrapper, let's have a look at what's left to do:
- Implement an initializer that takes Animal<F>
- Box Animal<F> into _AnyAnimalBox<A>
- Store the boxed animal as _AnyAnimalBase<F> (remember, we can't use A as a generic type, but we can use F)
- Forward all calls to from the public wrapper to the box:
final class AnyAnimal<T>: Animal where T: Food {
typealias FoodType = T
private let box: _AnyAnimalBase<T>
init<A: Animal>(_ animal: A) where A.FoodType == T {
box = _AnyAnimalBox(animal)
}
// Call forwarding for implementing Animal
var preferredFood: T? {
get { return box.preferredFood }
set { box.preferredFood = newValue }
}
var name: String {
return box.name
}
func eat(food: T) {
box.eat(food: food)
}
}
Let's have a bit of fun now with our newly implemented type erasure pattern.
First, let's redefine our base types, Cow and Goat:
struct Cow: Animal {
var name: String
var preferredFood: Grass? = nil
}
struct Goat: Animal {
var name: String
var preferredFood: Grass? = nil
}
Then, as these two grass eaters eat the same way, we can provide a conditional default implementation on Animal:
extension Animal where FoodType: Grass {
func eat(food: FoodType) {
if let preferredFood = preferredFood,
type(of: food) == type(of: preferredFood) {
print("\(name): Yummy! \(type(of: food))")
} else {
print("\(name): I'm eating...")
}
}
}
Now all of the pieces are together and we can go to the pasture with our flock of grass eaters, and let them enjoy their favorite grass:
class Grass: Food {}
class Flower: Grass {}
class Dandelion: Grass {}
class Shamrock: Grass {}
let flock = [
AnyAnimal(Cow(name: "Bessie", preferredFood: Dandelion())),
AnyAnimal(Cow(name: "Henrietta", preferredFood: nil)),
AnyAnimal(Goat(name: "Billy", preferredFood: Shamrock())),
AnyAnimal(Goat(name: "Nanny", preferredFood: Flower()))
]
let flowers = [
Grass(),
Dandelion(),
Flower(),
Shamrock()
]
while true {
flock.randomElement()?
.eat(food: flowers.randomElement()!)
sleep(1)
}
Running this program should print something similar to the following:
Bessie: I'm eating...
Nanny: Yummy! Flower
Henrietta: I'm eating...
Bessie: I'm eating...
Billy: I'm eating...
Henrietta: I'm eating...
Nanny: I'm eating...
Nanny: Yummy! Flower
...