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 voaAqui, 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.
Desafio
FácilUm cavaleiro não herda de uma arma: um cavaleiro tem uma arma e pode trocá-la. Crie as duas classes.
Weaponarmazenaweapon_nameedamage, ambos definidos pelo construtor:Weapon.new("Sword", 7).Knightarmazenaknight_name(do construtor) e uma arma, que começa comonull.equip(new_weapon)substitui a arma edrop()a define novamente comonull.attack()retornaAda attacks with Sword for 7 damage, usando o nome do cavaleiro e o nome e o dano da arma, ouAda has no weaponquando 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 weaponSeu 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())
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
1Fundamentos da POO
Arquivos externosIntrodução à POOClasses vs. objetosA palavra-chave selfMétodosPropriedadesConstrutor (_init)Revisão - Calculadora simples4Polimorfismo
Revisitando a Sobrescrita de MétodosDuck TypingClasses e Métodos AbstratosDesign de InterfacesRevisão - Calculadora de Formas7Sinais
O que é um sinal?Declarando e emitindoConectando-se a sinaisSinais com argumentosRevisão - Sistema de saúde10Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite2Propriedades de Classe
Variáveis de Instância vs. EstáticasFunções EstáticasMétodos Setters de PropriedadeMétodos Getters de PropriedadePrivado por ConvençãoRecapitulando: Gerenciador de Conta Bancária5Encapsulamento
Membros públicos e privadosValidação com settersOcultação de informaçõesPropriedades somente para leituraRevisão - Sistema de registros de alunos8Conceitos Avançados de POO
Composição vs. HerançaClasses InternasNós como ComponentesRefCounted vs. NodeEnums em Classes3Herança
Herança básicaA palavra-chave superSobrescrita de métodosCadeias de herançaVerificações de tipo com isRevisão - Hierarquia de funcionários6Métodos especiais
Introdução aos métodos virtuaisO método _to_stringIteradores personalizadosRevisão - Lista personalizada9Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão StrategyPratique por conta própria: Compilador de GDScript online