Menu
CoddyTech

Composição vs. Herança

Parte da seção Programação orientada a objetos do Journey de GDScript da Coddy. Lição 40 de 65.

As classes reutilizam código de duas maneiras. Herança (extends) diz que uma classe é um tipo de outra classe: um Mage é um Player. Composição diz que uma classe tem uma parte: ela armazena outro objeto em uma variável membro e delega trabalho a ele:

# 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() retorna Car starts, motor running. O carro não sabe como um motor funciona: ele chama motor.start(). Encaminhar uma chamada para uma parte como essa é chamado de delegação. (A classe se chama Motor porque Engine já é uma classe do Godot.)

A composição vale a pena quando as partes mudam ou se combinam. Com herança, um personagem que anda e um personagem que voa são duas subclasses, e um morcego que começa a voar não pode mudar de classe. Com composição, o personagem tem uma parte de movimento, e trocá-la exige apenas uma atribuição:

# 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 anda
bat.movement = Fly.new()
print(bat.move()) # Bat voa

Aqui, Walk e Fly estendem uma pequena classe Movement: as duas técnicas funcionam juntas. Uma classe GDScript pode extend apenas uma classe, então a composição também é uma forma de um objeto obter várias habilidades, mantendo várias partes.

Use herança para uma relação real de "é um" que nunca muda. Use composição quando a classe "tem" algo ou quando a parte precisa ser substituída enquanto o jogo está em execução.

challenge icon

Desafio

Fácil

Um cavaleiro não herda de uma arma: um cavaleiro tem uma arma e pode trocá-la. Crie as duas classes.

  • Weapon armazena weapon_name e damage, ambos definidos pelo construtor: Weapon.new("Sword", 7).
  • Knight armazena knight_name (do construtor) e uma arma, que começa como null.
  • equip(new_weapon) substitui a arma e drop() a define novamente como null.
  • attack() retorna Ada attacks with Sword for 7 damage, usando o nome do cavaleiro e o nome e o dano da arma, ou Ada has no weapon quando não há nenhuma.

O código fornecido lê o nome do cavaleiro e quantos comandos vêm em seguida (equip Sword 7, drop, attack) e os executa. Para Ada e attack, equip Sword 7, attack, equip Axe 12, attack, drop, attack, ele imprime:

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

Seu código deve ficar em weapon.gd e knight.gd. main.gd contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

extends Node

func _ready():
	# Código de entrada/saída fornecido: mantenha-o como está
	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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação orientada a objetos

Pratique por conta própria: Compilador de GDScript online