Утиная типизация
Часть раздела Объектно-ориентированное программирование путешествия по GDScript на Coddy. Урок 22 из 65.
Утиная типизация учитывает, что объект умеет делать, а не к какому классу он относится. Если у него есть вызываемый вами метод, вызов сработает. Название происходит от поговорки: «Если оно плавает как утка и крякает как утка, значит, это утка».
У этих двух классов нет общего родительского класса, но у обоих есть методы swim() и quack():
# duck.gd
class_name Duck
extends RefCounted
func swim() -> String:
return "Duck swimming"
func quack() -> String:
return "Quack!"# robot.gd
class_name Robot
extends RefCounted
func swim() -> String:
return "Robot swimming with propellers"
func quack() -> String:
return "Robot sound: BEEP BEEP!"Параметр без типа принимает любой объект. Функция вызывает методы, и каждый объект выполняет свои:
func make_it_swim_and_quack(thing) -> void:
print(thing.swim())
print(thing.quack())make_it_swim_and_quack(Duck.new())
make_it_swim_and_quack(Robot.new())Duck swimming
Quack!
Robot swimming with propellers
Robot sound: BEEP BEEP!Для переопределения нужен общий родительский класс; для утиной типизации — нет. Если параметр имеет тип thing: Duck, передача Robot вызовет ошибку разбора.
Если у объекта отсутствует метод, вызов прерывает выполнение функции с ошибкой, например Invalid call. Nonexistent function 'quack'. Сначала проверьте наличие метода с помощью has_method(), которому передаётся имя метода в виде String:
var things := [Duck.new(), Rock.new()]
for thing in things:
if thing.has_method("quack"):
print(thing.quack())
else:
print("This one cannot quack")Quack!
This one cannot quackЦена: при использовании типов Godot находит неверное имя метода до запуска программы; при использовании утиной типизации вы узнаёте об этом, только когда выполняется эта строка. Используйте её для объектов несвязанных классов, у которых есть несколько общих методов.
Задание
ЛегкоВзрыв повреждает всё поблизости, что может получить урон. У объектов нет общего родительского класса. player.gd и rock.gd готовы: у Player есть take_damage(amount) и status(), а у Rock — только status().
- В
crate.gdдобавь вCrateпеременнуюbroken, изначально равнуюfalse, методtake_damage(amount), который ломает ящик, если урон равен10или больше, и методstatus(), возвращающийCrate: brokenилиCrate: intact. - В
explosion.gdнапиши статический методhit_all(things, amount). Он вызываетtake_damage(amount)у каждого объекта в Array, у которого есть этот метод, и возвращает количество объектов, по которым он попал.
Предоставленный код считывает строку с именами объектов (player, crate или rock), затем количество взрывов и урон от каждого из них. Он выводит, по скольким объектам попал каждый взрыв, а затем статус каждого объекта. Для player crate rock и взрывов с уроном 5 и 20 он выводит:
Explosion 1 hit 2 objects
Explosion 2 hit 2 objects
Player: 75 HP
Crate: broken
Rock: solidТвой код нужно написать в crate.gd и explosion.gd. В main.gd находится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
extends Node
func _ready():
# Предоставленный код ввода/вывода: оставьте его без изменений
var things := []
for word in OS.read_string_from_stdin().split(" "):
if word == "player":
things.append(Player.new())
elif word == "crate":
things.append(Crate.new())
else:
things.append(Rock.new())
var count := int(OS.read_string_from_stdin())
for i in count:
var hits := Explosion.hit_all(things, int(OS.read_string_from_stdin()))
print("Explosion %d hit %d objects" % [i + 1, hits])
for thing in things:
print(thing.status())
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово selfМетодыСвойстваКонструктор (_init)Повторение: простой калькулятор4Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур7Сигналы
Что такое сигнал?Объявление и отправкаПодключение к сигналамСигналы с аргументамиПовторение — система здоровья10Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»2Свойства классов
Переменные экземпляра и классаСтатические функцииСеттеры свойствГеттеры свойствПриватность по соглашениюПовторение — менеджер банковского счёта5Инкапсуляция
Открытые и закрытые членыПроверка данных с помощью сеттеровСокрытие информацииСвойства только для чтенияПовторение — система учета студентов8Продвинутые концепции ООП
Композиция и наследованиеВнутренние классыУзлы как компонентыRefCounted и NodeПеречисления в классах11Проект: Управление библиотекой
Обзор проектаКлассы книг и пользователей3Наследование
Основы наследованияКлючевое слово superПереопределение методовЦепочки наследованияПроверка типов с помощью isПовторение — иерархия сотрудников6Специальные методы
Введение в виртуальные методыМетод _to_stringПользовательские итераторыПовторение: пользовательский список9Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор GDScript