В недавней записи в блоге я показал несколько примеров того, как (и почему) можно обойти так называемое ограничение отсутствия наследования в Pony. Чтобы проиллюстрировать это, я показал, как мы можем создавать акторы, которые демонстрируют одно и то же поведение, создав трейт для описания этого поведения, например этот:
trait Container be enter_inv(ob: Any tag, from: Any tag) be leave_inv(ob: Any tag, to: Any tag)
Это позволило нам создать акторов, которые реализуют черту, чтобы с ними можно было обращаться одинаково, но нам все же пришлось немного уродовать, реализовав эти поведения:
be enter_inv(ob: Any tag, from: Any tag) => // game object entered my inventory _out.print("object entered my inventory") _container.add(ob) be leave_inv(ob: Any tag, to: Any tag) => // game object left my inventory _out.print("Object left my inventory") _container.remove(ob)
Это достигло нашей цели, но мы можем добиться большего. Здесь мы можем использовать трюк, который обеспечит реализацию по умолчанию для любых акторов, которые просто хотят реализовать инвентаризацию без каких-либо настроек.
Давайте взглянем на обновленную версию трейта Container:
trait Container be enter_inv(ob: Any tag, from: Any tag) => invstorage().add(ob) be leave_inv(ob: Any tag, to: Any tag) => invstorage().remove(ob) fun ref invstorage() : ActorStorage
Здесь есть пара интересных вещей. Во-первых, обратите внимание, что теперь есть реализация по умолчанию, связанная с первыми двумя поведениями. Если вы использовали трейты или примеси Scala в других языках, эта концепция должна показаться вам знакомой.
Каждая из этих реализаций фактически вызывает другой метод для одного и того же трейта, invstorage(). Это возвращает объект ActorStorage. С таким же успехом мы могли бы сделать ActorStorage еще одним трейтом, чтобы базовая реализация хранилища акторов могла изменяться без изменения трейта контейнера.
Теперь наш игрок, NPC, магазин и другие актеры могут получить реализацию контейнера по умолчанию, просто предоставив средства хранения инвентаря с помощью такого метода:
fun ref invstorage(): ActorStorage => _container
Это дает нам огромную мощность и гибкость. Вместо того, чтобы работать в сложной иерархии унаследованных классов, имея дело с родителями, братьями и сестрами и искусственными ограничениями, мы можем вместо этого решить, какие группы поведения мы хотим встроить в наших актеров.
Сочетание этой стратегии раскрытия класса данных со свойствами, субъектами, безопасностью типов Pony и системой субъектов дает разработчикам преимущество, которое нельзя упускать из виду.