Я согласен с Кристиком по поводу добавления ластика типа AnyMover.Это способ сделать буквально то, что вы говорите.Но если вы идете по этому пути, вы, как правило, неправильно разработали свои протоколы.Например, хотя я знаю, что это сфабриковано, это отличный пример неправильно разработанного протокола.Протокол Животного почти наверняка должен быть:
protocol Animal {
func move()
}
А затем ваши associatedtype
и связанные с ними проблемы испаряются.И ваш интерфейс имеет больше смысла.Животное может двигаться.Это не что-то "с двигателем".Я ожидаю, что каждое использование этого типа будет выглядеть почти идентично вашему примеру: animal.mover.move()
.Это говорит о том, что mover
- это деталь реализации, которая не должна волновать звонящих.
Что еще может сделать в цикле над [Animal]
?Как вы могли бы написать общий код, который использовал бы .mover
и не вызов .move
?Других методов не существует.
Я знаю, что это сфабриковано, но именно эта ситуация и возникает во многих реальных случаях, и вы должны быть настороже.Когда вы обнаруживаете, что тянетесь к стирателям, особенно если вы начинаете тянуться к двум уровням стирателей, вам нужно спросить, не сделали ли вы что-то не так.Не всегда, но в большинстве случаев перед вами стоит лучшее решение.
Кстати, еще один подход - сохранить Movers, но просто позволить им быть протоколом.Звонящий действительно хочет знать разницу между сканерами и слайтерами?Разве это не скрывает цели?Если это так, вы можете пойти по этому пути:
protocol Animal {
var mover: MoverType { get }
}
И снова все проблемы испаряются.
В любом случае, вы все равно можете автоматически реализовать свой метод move()
, если есть mover
доступный.Например, вы можете разработать протоколы следующим образом:
// Something that moves animals
protocol Mover {
func move(animal: Animal)
}
// Something that has a mover
protocol MoverProviding {
var mover: Mover { get }
}
// And of course Animals. They might be MoverProviding. They might not.
protocol Animal {
func move()
}
// But if they *are* MoverProviding, we can use that.
extension Animal where Self: MoverProviding {
func move() {
mover.move(animal: self)
}
}
Когда вы набираете associatedtype
, вы должны думать: «Этот протокол предназначен для добавления дополнительных алгоритмов (методов и функций) к другим типам."Если цель протокола - разрешить разнородные коллекции, вы, вероятно, на неправильном пути.Стираторы типов иногда полезны и важны, но если вы чувствуете, что они вам очень нужны (и особенно , если они вам нужны из-за разнородных коллекций), у вас, вероятно, есть проблема с дизайном.