Menu
Coddy logo textTech

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
end

Ao 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)  -- 200

O 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.

challenge icon

Desafio

Fácil

Vamos 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}GHz
  • Computer.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:

  1. A marca da CPU (uma string)
  2. A velocidade da CPU em GHz (um número)
  3. 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}GHz

Por exemplo, se as entradas forem Intel, 3.5, e WorkStation, a saída deve ser:

Model: WorkStation
CPU: Intel @ 3.5GHz

Observe 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
end

Criando e usando objetos compostos:

local myEngine = Engine:new(200)
local myCar = Car:new("Sedan", myEngine)

print(myCar.engine.horsepower)  -- 200

A 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
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 Object Oriented Programming