Menu
CoddyTech

Duck Typing

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

Tipagem pato se importa com o que um objeto pode fazer, não a qual classe ele pertence. Se ele tiver o método que você chama, a chamada funciona. O nome vem do ditado "se nada como um pato e grasna como um pato, é um pato".

Essas duas classes não compartilham um pai próprio, mas ambas têm swim() e 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!"

Um parâmetro sem tipo aceita qualquer objeto. A função chama os métodos, e cada objeto executa os seus:

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!

A sobrescrita precisa de uma classe pai em comum; a tipagem estrutural não. Com o parâmetro tipado como thing: Duck, passar um Robot causaria um erro de análise.

Quando um objeto não possui o método, a chamada interrompe a função com um erro como Invalid call. Nonexistent function 'quack'. Primeiro, verifique com has_method(), que recebe o nome do método como uma 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

O preço: com tipos, Godot encontra um nome de método incorreto antes de o programa ser executado; com tipagem dinâmica, você só descobre quando a linha é executada. Use-a para objetos de classes não relacionadas que compartilham alguns métodos.

challenge icon

Desafio

Fácil

Uma explosão danifica tudo por perto que possa sofrer dano. Os objetos não compartilham uma classe pai. player.gd e rock.gd estão prontos: um Player tem take_damage(amount) e status(), enquanto um Rock só tem status().

  • Em crate.gd, dê a Crate uma variável broken que começa como false, um método take_damage(amount) que quebra a caixa quando a quantidade é 10 ou mais, e status() retornando Crate: broken ou Crate: intact.
  • Em explosion.gd, escreva o método estático hit_all(things, amount). Ele chama take_damage(amount) em cada objeto do Array que tenha esse método e retorna quantos objetos atingiu.

O código fornecido lê uma linha com nomes de objetos (player, crate ou rock), depois quantas explosões haverá e o dano de cada uma. Ele imprime quantos objetos cada explosão atingiu e, em seguida, o status de cada objeto. Para player crate rock e explosões de 5 e 20, ele imprime:

Explosion 1 hit 2 objects
Explosion 2 hit 2 objects
Player: 75 HP
Crate: broken
Rock: solid

Seu código deve ficar em crate.gd e explosion.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 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())
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