Pular para o conteúdo

Relacionamentos entre objetos

Associação, agregação, composição, dependência — e por que a diferença importa.

Objetos se conhecem de jeitos diferentes, e o jeito decide quem cria quem, quem morre com quem, e o que quebra ao mudar.

As quatro formas#

ForçaCiclo de vidaExemplo
dependênciaa mais fracaindependentesum método recebe um Relatorio como parâmetro
associaçãofracaindependentesPedido conhece o Cliente
agregaçãomédiaindependentesTime tem Jogador; o jogador sobrevive ao time
composiçãofortemorrem juntosPedido tem ItemDePedido; sem pedido, o item não existe

Dependência#

A mais fraca: o objeto usa outro, mas não o guarda.

dataforge
blueprint Impressora:
    // 'documento' entra, é usado, e vai embora
    action imprimir(documento):
        yield $"imprimindo: {documento.titulo}"

record Documento:
    titulo: String

p := spawn Impressora()
assert p.imprimir(Documento("Contrato")) is "imprimindo: Contrato"

Associação#

O objeto guarda uma referência ao outro, mas os dois existem por conta própria:

dataforge
blueprint Cliente:
    action setup(nome):
        self.nome := nome

blueprint Pedido:
    action setup(cliente, total):
        self.cliente := cliente
        self.total := total

    get resumo():
        yield $"{self.cliente.nome}: {self.total}"

ana := spawn Cliente("Ana")
p1 := spawn Pedido(ana, 100)
p2 := spawn Pedido(ana, 250)

// um cliente, dois pedidos — e o cliente existia antes deles
assert p1.resumo is "Ana: 100"
assert p2.cliente is p1.cliente

Agregação#

"Tem um", mas a parte sobrevive ao todo:

dataforge
blueprint Jogador:
    action setup(nome):
        self.nome := nome

blueprint Time:
    action setup(nome):
        self.nome := nome
        self.jogadores := []

    action contratar(jogador):
        self.jogadores.append(jogador)
        yield len(self.jogadores)

    action liberar(jogador):
        self.jogadores.remove(jogador)
        yield len(self.jogadores)

j := spawn Jogador("Pelé")
t := spawn Time("Santos")
t.contratar(j)
t.liberar(j)

// o time acabou com ele; o jogador continua existindo
assert j.nome is "Pelé"

Composição#

"É feito de": a parte nasce e morre com o todo, e não é compartilhada:

dataforge
record ItemDePedido:
    produto: String
    quantidade: Integer
    preco: Float

    action subtotal():
        yield self.quantidade * self.preco

blueprint Pedido:
    action setup():
        self.itens := []

    // o pedido CRIA seus itens — eles não vêm de fora
    action adicionar(produto, quantidade, preco):
        self.itens.append(ItemDePedido(produto, quantidade, preco))
        yield len(self.itens)

    get total():
        soma := 0.0
        cycle i in self.itens:
            soma += i.subtotal()
        yield soma

p := spawn Pedido()
p.adicionar("Teclado", 2, 250.0)
p.adicionar("Mouse", 1, 90.0)
assert p.total is 590.0

Cardinalidade#

Quantos de cada lado. É o que vira chave estrangeira no banco:

NotaçãoSignificaNo ORM
1 → 1um para umtem_um
1 → Num para muitostem_muitos
N → 1muitos para umpertence_a
N → Nmuitos para muitosmuitos_para_muitos, com tabela ponte
dataforge
// Cliente 1 ─── N Pedido ─── N Produto
adopt Forge

db := Forge.conectar(":memory:")

Cliente := Forge.modelo("Cliente", {
    "id": {"tipo": "Serial"},
    "nome": {"tipo": "Texto", "obrigatorio": yes}
}, {"conexao": db})

Pedido := Forge.modelo("Pedido", {
    "id": {"tipo": "Serial"},
    "cliente_id": {"tipo": "Inteiro", "indice": yes},
    "total": {"tipo": "Real", "padrao": 0}
}, {"conexao": db})

Cliente.tem_muitos("pedidos", "Pedido")
Pedido.pertence_a("cliente", "Cliente")
Forge.migrar_tudo(db)

ana := Cliente.criar({"nome": "Ana"})
Pedido.criar({"cliente_id": ana["id"], "total": 100.0})

com_pedidos := Cliente.com(Cliente.todos(), "pedidos")
assert len(com_pedidos[0]["pedidos"]) is 1

Uma associação pode ser unidirecional (o pedido conhece o cliente, mas o cliente não lista pedidos) ou bidirecional (os dois se conhecem).

Bidirecional é mais conveniente e mais caro: os dois lados precisam ficar consistentes, e há sempre o risco de um ciclo na serialização.