我有许多饥饿的FoodMonster
人可以munch
许多不同类型的食物。怪物对每种类型的munch
都有单独的重载方法。
我希望建立一个管理很多怪物的MonsterPack
,并且可以为FoodMonster
munch
protocol Cookie {}
protocol Lettuce {}
protocol FoodMonster {
func munch(Cookie)
func munch(Lettuce)
}
struct MonsterPack {
var monsters = [FoodMonster]()
func feed<T>(food: T) {
for monster in monsters {
monster.munch(food)
}
}
}
提供所有类型的食物。我已经实现了这样:
MonsterPack.feed
不幸的是,这个FoodMonster
的实现没有编译,因为它是通用的,munch
不能像他们想要的那样feed
任何类型的值。
我有什么方法可以定义FoodMonster
,以便我可以使用munch
可以 {{1}}的任何值来调用它吗?
答案 0 :(得分:2)
首先将Lettuce
和Cookie
之间的共同点视为Food
。
protocol Food {}
protocol Lettuce : Food {}
protocol Cookie : Food {}
然后指定你feed
怪物food
:
func feed<T:Food>(food: T) {
for monster in monsters {
monster.munch(food)
}
}
并使munch
本身通用为:
protocol FoodMonster {
func munch<T:Food> (food:T)
}
答案 1 :(得分:1)
GoZoner的解决方案很有吸引力,但实际上只是子类化。事实上,它与此相同(注意缺少T
):
protocol Food {}
protocol Lettuce : Food {}
protocol Cookie : Food {}
protocol FoodMonster {
func munch(food: Food)
}
struct MonsterPack {
var monsters = [FoodMonster]()
func feed(food: Food) {
for monster in monsters {
monster.munch(food)
}
}
}
根本不需要仿制药。在这种情况下,类型参数化确实没有意义,因为您只是依赖于协议。没有理由说“T,T是食物。”那就是“食物”。 (这种过度参数化非常普遍,你应该经常观察它。在你只是意味着“符合”的情况下使用泛型很容易。)
如果你可以使用一个协议,使Lettuce和Cookie都有一些共同的方法,那真是太棒了。但是在给定的例子中这是不可能的。我没有办法在Cookie上调用。我从你的问题中得知,那里确实有一套特定的食物,一个怪物必须能够以特定的方式吃掉它们。如果是这种情况,那么任何真正的怪物都会在munch
中进行类型检查。这不是子类化。这是一个sum-type,在Swift中称为enum。
enum Food {
case Cookie
case Lettuce
}
protocol FoodMonster {
func munch(Food)
}
struct MonsterPack {
var monsters = [FoodMonster]()
func feed(food: Food) {
for monster in monsters {
monster.munch(food)
}
}
}
struct RealMonster {
func feed(food: Food) {
switch food {
case .Cookie: break // Do the cookie thing
case .Lettuce: break // Do the lettuce thing
}
}
}
现在我所说的是“如果你想成为一个怪物,你将必须能够咀嚼所有这些可能没有共同协议的东西。”如果这就是你的意思,那么enum就是你的工具,而不是子类,协议或泛型。