Menu
Coddy logo textTech

Incorporação vs agregação

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 38 de 107.

Você aprendeu que a incorporação promove campos e métodos para a struct externa. Mas Go também oferece suporte à agregação, na qual você inclui outra struct como um campo nomeado. Entender quando usar cada abordagem é fundamental para um bom design.

Com o embedding, você omite o nome do campo, e os membros do tipo interno se tornam diretamente acessíveis:

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // embedding - sem nome de campo
}

d := Document{}
d.Write()  // acesso direto

Com a agregação, você dá ao campo um nome explícito, exigindo esse nome para acessar o tipo interno:

type Document struct {
    writer Writer  // agregação - campo nomeado
}

d := Document{writer: Writer{}}
d.writer.Write()  // deve usar o nome do campo

A principal diferença é o relacionamento que você está expressando. Embedding sugere um relacionamento “is-a”, em que o tipo externo assume a identidade do tipo interno. Aggregation sugere um relacionamento “has-a”, em que o tipo externo apenas contém o tipo interno.

Use incorporação quando quiser que o tipo externo satisfaça as interfaces implementadas pelo tipo interno ou quando o acesso direto aos métodos tornar a API mais limpa. Use agregação quando o tipo interno for um detalhe de implementação que não deve ser exposto ou quando quiser controle explícito sobre como o tipo interno é acessado.

challenge icon

Desafio

Fácil

Vamos criar um sistema de geração de relatórios que demonstra quando usar incorporação em vez de agregação. Você criará um sistema no qual alguns componentes devem ser acessíveis diretamente (incorporação), enquanto outros devem permanecer como detalhes internos de implementação (agregação).

Você organizará seu código em três arquivos:

  • formatter.go: Crie uma struct Formatter com um campo Style (string) e um método Format(text string) string que retorna o texto envolvido pelo estilo, como [Style] text [/Style]. Este será um auxiliar interno que não deve ser exposto diretamente.
  • report.go: Crie duas structs que lidam com a geração de relatórios de maneiras diferentes:
    • Uma struct Metadata com os campos Author e Date. Ela representa informações públicas que devem ser acessíveis diretamente
    • Uma struct Report com um campo Title que incorpora Metadata (para acesso direto ao autor e à data), mas usa agregação para Formatter (como um detalhe interno de implementação com um campo nomeado formatter)
    Adicione um método Generate() string a Report que use o formatador interno para formatar o título e inclua os metadados.
  • main.go: Leia os detalhes do relatório a partir da entrada, crie um Report com metadados incorporados e um formatador agregado. Demonstre a diferença acessando diretamente o autor no relatório (por meio da incorporação) e, em seguida, gerando o relatório formatado (que usa internamente o formatador oculto).

As seguintes entradas serão fornecidas:

  • Linha 1: Título do relatório
  • Linha 2: Nome do autor
  • Linha 3: Data
  • Linha 4: Estilo do formatador

Seu método Format deve retornar:

[Style] text [/Style]

Seu método Generate deve retornar:

[formatted title]
Author: [Author], Date: [Date]

No arquivo principal, imprima três linhas:

  1. O autor acessado diretamente no Report (demonstrando que a incorporação promove o campo)
  2. A data acessada por meio do nome do tipo incorporado (report.Metadata.Date)
  3. O resultado da chamada de Generate()

Por exemplo, considerando Quarterly Results, Jane Doe, 2024-01-15 e BOLD, sua saída deve ser:

Jane Doe
2024-01-15
[BOLD] Quarterly Results [/BOLD]
Author: Jane Doe, Date: 2024-01-15

Observe como Author e Date são acessíveis diretamente no Report por meio da incorporação, enquanto o formatador permanece oculto. Você não pode chamar report.Format() diretamente porque ele foi agregado com um campo nomeado. Isso demonstra, na prática, a relação “é um” versus “tem um”.

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	title := scanner.Text()
	
	scanner.Scan()
	author := scanner.Text()
	
	scanner.Scan()
	date := scanner.Text()
	
	scanner.Scan()
	style := scanner.Text()
	
	// TODO: Crie uma struct Metadata com o author e a date
	
	// TODO: Crie um Formatter com o style fornecido
	
	// TODO: Crie um Report que embute Metadata e agrega Formatter
	
	// TODO: Imprima o author acessado diretamente no Report (embedding promove o campo)
	
	// TODO: Imprima a date acessada através do nome do tipo embutido (report.Metadata.Date)
	
	// TODO: Imprima o resultado de chamar Generate()
	
	// Suprima avisos de variáveis não utilizadas (remova estes quando você usar as variáveis)
	_ = title
	_ = author
	_ = date
	_ = style
}
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 Programação Orientada a Objetos

Pratique por conta própria: Compilador de Go online