Menu
CoddyTech

Composición frente a herencia

Parte de la sección Programación orientada a objetos del Journey de GDScript de Coddy. Lección 40 de 65.

Las clases reutilizan código de dos maneras. La herencia (extends) indica que una clase es un tipo de otra clase: un Mage es un Player. La composición indica que una clase tiene una parte: almacena otro objeto en una variable miembro y le delega el trabajo:

# 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() devuelve Car starts, motor running. El coche no sabe cómo funciona un motor: llama a motor.start(). Pasar una llamada a una pieza de esta manera se llama delegación. (La clase se llama Motor porque Engine ya es una clase de Godot.)

La composición resulta útil cuando las partes cambian o se combinan. Con la herencia, un personaje que camina y uno que vuela son dos subclases, y un murciélago que empieza a volar no puede cambiar de clase. Con la composición, el personaje tiene una parte de movimiento, y cambiarla requiere una sola asignación:

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

Aquí, Walk y Fly extienden una pequeña clase Movement: las dos técnicas funcionan juntas. Una clase GDScript solo puede extend una clase, así que la composición también permite que un objeto tenga varias habilidades al contener varias partes.

Usa la herencia para una relación «es un» real que nunca cambia. Usa la composición cuando la clase «tiene» algo, o cuando haya que cambiar la parte mientras se ejecuta el juego.

challenge icon

Desafío

Fácil

Un caballero no hereda de un arma: un caballero tiene un arma y puede cambiarla. Crea ambas clases.

  • Weapon almacena weapon_name y damage, ambos definidos por el constructor: Weapon.new("Sword", 7).
  • Knight almacena knight_name (del constructor) y un arma, que al principio es null.
  • equip(new_weapon) reemplaza el arma y drop() la vuelve a establecer en null.
  • attack() devuelve Ada attacks with Sword for 7 damage, usando el nombre del caballero y el nombre y el daño del arma, o Ada has no weapon cuando no hay ninguna.

El código proporcionado lee el nombre del caballero y cuántos comandos vienen después (equip Sword 7, drop, attack) y los ejecuta. Para Ada y attack, equip Sword 7, attack, equip Axe 12, attack, drop, attack, 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

Tu código va en weapon.gd y knight.gd. main.gd contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

extends Node

func _ready():
	# Código de entrada/salida proporcionado: mantenlo tal 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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Compilador de GDScript online