Menu
CoddyTech

Композиция и наследование

Часть раздела Объектно-ориентированное программирование путешествия по GDScript на Coddy. Урок 40 из 65.

Классы повторно используют код двумя способами. Наследование (extends) означает, что класс является разновидностью другого класса: Mage — это Player. Композиция означает, что класс содержит часть: он хранит другой объект в переменной-члене и делегирует ему работу:

# motor.gd
class_name Motor
extends RefCounted

func start() -> String:
	return "motor running"

# car.gd
class_name Car
extends RefCounted

var motor := Motor.new()

func start() -> String:
	return "Car starts, " + motor.start()

Car.new().start() возвращает Car starts, motor running. Машина не знает, как работает мотор: она вызывает motor.start(). Передача вызова такой части называется делегированием. (Класс называется Motor, потому что Engine — уже существующий класс Godot.)

Композиция окупается, когда части изменяются или объединяются. При наследовании персонаж, который ходит, и персонаж, который летает, — это два подкласса, а летучая мышь, начинающая летать, не может изменить свой класс. При композиции у персонажа есть компонент передвижения, и его переключение — это одно присваивание:

# character.gd
class_name Character
extends RefCounted

var char_name: String
var movement: Movement

func _init(new_name: String, new_movement: Movement) -> void:
	char_name = new_name
	movement = new_movement

func move() -> String:
	return char_name + " " + movement.move()

# main.gd
var bat := Character.new("Bat", Walk.new())
print(bat.move()) # Bat ходит
bat.movement = Fly.new()
print(bat.move()) # Bat летает

Здесь Walk и Fly расширяют небольшой класс Movement: эти две техники работают вместе. Класс GDScript может наследоваться только от одного класса с помощью extend, поэтому композиция — это также способ наделить один объект несколькими возможностями, объединив в нём несколько частей.

Используй наследование для настоящего отношения «является», которое никогда не меняется. Используй композицию, когда у класса что-то «есть» или когда часть нужно заменять во время игры.

challenge icon

Задание

Легко

Рыцарь не наследуется от оружия: у рыцаря есть оружие, и он может его сменить. Создайте оба класса.

  • Weapon хранит weapon_name и damage, оба задаются конструктором: Weapon.new("Sword", 7).
  • Knight хранит knight_name (из конструктора) и оружие, которое в начале равно null.
  • equip(new_weapon) заменяет оружие, а drop() снова присваивает ему null.
  • attack() возвращает Ada attacks with Sword for 7 damage, используя имя рыцаря, а также имя и урон оружия, либо Ada has no weapon, если оружия нет.

Предоставленный код считывает имя рыцаря и количество следующих команд (equip Sword 7, drop, attack), а затем выполняет их. Для Ada и команд attack, equip Sword 7, attack, equip Axe 12, attack, drop, attack он выводит:

Ada has no weapon
Equipped Sword
Ada attacks with Sword for 7 damage
Equipped Axe
Ada attacks with Axe for 12 damage
Dropped the weapon
Ada has no weapon

Ваш код нужно поместить в weapon.gd и knight.gd. В main.gd находится предоставленный код ввода/вывода, его нельзя редактировать.

Попробуйте сами

extends Node

func _ready():
	# Предоставленный код ввода/вывода: оставьте его без изменений
	var knight := Knight.new(OS.read_string_from_stdin())
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "equip":
			knight.equip(Weapon.new(parts[1], int(parts[2])))
			print("Equipped ", parts[1])
		elif parts[0] == "drop":
			knight.drop()
			print("Dropped the weapon")
		else:
			print(knight.attack())
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор GDScript