Relacionamento Tem-Um
Parte da seção Object Oriented Programming do Journey de Lua da Coddy — lição 49 de 70.
Ao longo deste curso, você usou herança para compartilhar comportamento entre classes—um Dog "é um" Animal. Mas a herança nem sempre é a ferramenta certa.
Às vezes, os objetos não compartilham uma árvore genealógica; em vez disso, um objeto simplesmente contém outro. Isso é chamado de composição, e modela um relacionamento "tem-um".
Considere um carro. Um carro não é um tipo de motor — ele tem um motor. O motor é um objeto separado que existe dentro do carro:
local Engine = {}
Engine.__index = Engine
function Engine:new(horsepower)
local obj = {horsepower = horsepower}
setmetatable(obj, Engine)
return obj
end
local Car = {}
Car.__index = Car
function Car:new(model, engine)
local obj = {
model = model,
engine = engine -- Armazena o objeto Engine dentro de Car
}
setmetatable(obj, Car)
return obj
endAo criar um carro, você passa um objeto de motor existente para o seu construtor:
local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)
print(myCar.engine.horsepower) -- 200O carro não herda do motor — ele simplesmente mantém uma referência a um. Isso mantém ambas as classes independentes. Você poderia trocar por um motor diferente, ou usar a mesma classe de motor em uma motocicleta.
A composição oferece uma flexibilidade que a herança não consegue proporcionar: você está montando objetos a partir de partes, em vez de prendê-los em uma hierarquia familiar rígida.
Desafio
FácilVamos construir um sistema de computador usando composição! Em vez de herança, você criará um Computer que tem uma CPU dentro dele — demonstrando o relacionamento "tem-um" (has-a).
Você organizará seu código em três arquivos:
CPU.lua: Crie uma classe CPU com um construtor:new(brand, speed)que armazena o nome da marca e a velocidade em GHz. Inclua um método:getInfo()que retorna uma string no formato:{brand} @ {speed}GHzComputer.lua: Crie uma classe Computer cujo construtor:new(model, cpu)aceita um nome de modelo e um objeto CPU. O Computer armazena o objeto CPU dentro de si (composição!). Inclua um método:describe()que imprime o modelo do computador e as informações de sua CPU.main.lua: Junte tudo! Leia as entradas para a marca da CPU, a velocidade da CPU e o modelo do computador. Crie um objeto CPU primeiro e, em seguida, passe-o para o construtor do Computer. Por fim, chame o método:describe()do computador.
Você receberá três entradas:
- A marca da CPU (uma string)
- A velocidade da CPU em GHz (um número)
- O nome do modelo do computador (uma string)
Sua saída deve mostrar a descrição do computador, que inclui informações de sua CPU contida:
Model: {model}
CPU: {brand} @ {speed}GHzPor exemplo, se as entradas forem Intel, 3.5, e WorkStation, a saída deve ser:
Model: WorkStation
CPU: Intel @ 3.5GHzObserve como o Computer não herda de CPU — ele simplesmente mantém uma referência a uma. O método :describe() do Computer acessa os dados da CPU por meio do objeto armazenado, mostrando como a composição permite construir objetos complexos a partir de partes mais simples!
Folha de consulta
Objetos podem se relacionar por meio da composição, que modela um relacionamento do tipo "tem um". Em vez de herança, um objeto contém outro como um componente.
Exemplo de composição com um carro que possui um motor:
local Engine = {}
Engine.__index = Engine
function Engine:new(horsepower)
local obj = {horsepower = horsepower}
setmetatable(obj, Engine)
return obj
end
local Car = {}
Car.__index = Car
function Car:new(model, engine)
local obj = {
model = model,
engine = engine -- Armazena o objeto Engine dentro de Car
}
setmetatable(obj, Car)
return obj
endCriando e usando objetos compostos:
local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)
print(myCar.engine.horsepower) -- 200A composição oferece flexibilidade ao montar objetos a partir de partes, em vez de usar hierarquias de herança rígidas. Os componentes permanecem independentes e podem ser reutilizados em diferentes classes.
Experimente você mesmo
-- main.lua
-- Reúne tudo usando composição
local CPU = require('CPU')
local Computer = require('Computer')
-- Ler entradas
local cpuBrand = io.read()
local cpuSpeed = tonumber(io.read())
local computerModel = io.read()
-- TODO: Criar um objeto CPU com a marca e a velocidade
-- TODO: Criar um objeto Computer com o modelo e o objeto CPU
-- TODO: Chamar o método describe() do computador para imprimir a saída
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1O Conceito 'Self'
Tabelas com Funções'self' ExplícitoA Sintaxe de Dois-pontosPonto vs Dois-pontosRecapitulação - Movendo o Ponto4Projeto: Banco Digital
Configuração do ProjetoMétodo de Depósito7Polimorfismo e Sobrescrita
Sobrescrita de MétodosChamando Métodos da Classe PaiDuck TypingInterface ComumVerificando o TipoRecapitulação - Papéis de Funcionários10Projeto: Gerenciador de Formas
Configuração do ProjetoClasse Retângulo2Padrão de Protótipo de Classe
O Conceito de ProtótipoVinculando com __indexO Construtor :new()Inicializando AtributosInstâncias IndependentesRecapitulação - Fábrica de Carros5Sobrecarga de Operadores em POO
Somando ObjetosSubtraindo ObjetosConcatenando ObjetosComparando Objetos (<, >)Recapitulação - Matemática de Carteira8Encapsulamento
Convenções de NomenclaturaClosures para PrivacidadeAcesso via ClosuresTabelas Somente LeituraLógica de ValidaçãoRecapitulação - Cofre Seguro11Padrões de Projeto (Lite)
Funções FactoryTabela SingletonPadrão IteratorObserver (Listener)Recapitulação - Logger Factory3Estado e Comportamento do Objeto
Variáveis de InstânciaMétodos GetterMétodos SetterPropriedades CalculadasFormatação de StringsVerificações de IgualdadeRecapitulação - Nota do Aluno6Fundamentos de Herança
Configurando a HerançaHerdando MétodosEstendendo o ConstrutorAdicionando Métodos FilhosCompartilhado vs ÚnicoRecapitulação - Hierarquia de Formas9Composição e Mixins
Relacionamento Tem-UmDelegaçãoMixins SimplesAplicando Múltiplos MixinsMixins vs HerançaRecapitulação - Montagem de Robô