Menu
Coddy logo textTech

Padrão Composite

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

O padrão Composite permite tratar objetos individuais e grupos de objetos de maneira uniforme. Enquanto o State altera o comportamento com base no estado interno, o Composite constrói estruturas em árvore nas quais tanto as folhas quanto os contêineres compartilham a mesma interface.

Este padrão é perfeito para estruturas hierárquicas, como sistemas de arquivos, organogramas ou componentes de UI. Em Go, definimos uma interface comum que tanto os itens individuais quanto os contêineres implementam:

type Component interface {
    GetSize() int
}

type File struct {
    Name string
    Size int
}

func (f File) GetSize() int {
    return f.Size
}

O composto (contêiner) contém elementos filhos e implementa a mesma interface agregando seus resultados:

type Folder struct {
    Name     string
    Children []Component
}

func (f *Folder) Add(c Component) {
    f.Children = append(f.Children, c)
}

func (f Folder) GetSize() int {
    total := 0
    for _, child := range f.Children {
        total += child.GetSize()
    }
    return total
}

Agora você pode aninhar pastas dentro de pastas, e chamar GetSize() funciona de forma idêntica, seja para um único arquivo ou para uma árvore de diretórios inteira:

docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})

root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})

fmt.Println(root.GetSize())  // 175

Composite é ideal quando você precisa representar hierarquias parte-todo e quer que os clientes tratem objetos individuais e composições de forma idêntica.

challenge icon

Desafio

Fácil

Vamos criar um sistema de organograma usando o padrão Composite! Você criará uma estrutura na qual tanto funcionários individuais quanto departamentos (que contêm funcionários ou outros departamentos) podem ser tratados de maneira uniforme, perfeita para calcular os salários totais de qualquer parte da organização.

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

  • component.go: Defina a interface comum que tanto os indivíduos quanto os grupos implementarão.

    Crie uma interface OrgComponent com dois métodos:

    • GetSalary() int: retorna o salário total deste componente
    • GetName() string: retorna o nome do funcionário ou departamento
  • organization.go: Implemente os tipos folha (funcionário individual) e composto (departamento).

    Crie uma struct Employee com os campos Name (string) e Salary (int). Seu GetSalary() retorna seu próprio salário, e GetName() retorna seu nome.

    Crie uma struct Department com um campo Name (string) e uma slice Members que armazena itens OrgComponent. Adicione um método Add(c OrgComponent) para adicionar membros. Seu GetSalary() deve retornar a soma dos salários de todos os membros, e GetName() retorna o nome do departamento.

  • main.go: Construa uma estrutura organizacional e calcule os salários.

    Leia o número de funcionários. Para cada funcionário, leia seu nome e salário, criando instâncias de Employee. Em seguida, leia o número de departamentos. Para cada departamento, leia seu nome e a quantidade de índices de membros, seguida por esses índices (baseados em 0, referindo-se aos funcionários ou departamentos criados anteriormente, na ordem de criação).

    Depois de construir a estrutura, leia um índice final e imprima o nome e o salário total desse componente no formato: [name]: [salary]

As seguintes entradas serão fornecidas:

  • Número de funcionários, seguido pelo nome e salário de cada funcionário em linhas separadas
  • Número de departamentos, seguido pelo nome de cada departamento, pela quantidade de membros e pelos índices dos membros
  • Índice final a ser consultado

Por exemplo, dado:

3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3

Sua saída deve ser:

Engineering: 165000

E, dado:

4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4

Sua saída deve ser:

QA: 125000

E, dado:

2
Alice
50000
Bob
60000
0
1

Sua saída deve ser:

Bob: 60000

Observe como GetSalary() funciona de forma idêntica, independentemente de ser chamado em um único funcionário ou em um departamento inteiro: o padrão Composite permite tratar indivíduos e grupos de maneira uniforme por meio da mesma interface!

Experimente você mesmo

package main

import "fmt"

func main() {
	// Ler o número de funcionários
	var numEmployees int
	fmt.Scanln(&numEmployees)

	// Armazenar todos os componentes (funcionários e departamentos) na ordem de criação
	var components []OrgComponent

	// TODO: Ler o nome e o salário de cada funcionário
	// Criar instâncias de Employee e adicioná-las ao slice components
	for i := 0; i < numEmployees; i++ {
		var name string
		var salary int
		fmt.Scanln(&name)
		fmt.Scanln(&salary)
		// TODO: Criar employee e adicionar a components
	}

	// Ler o número de departamentos
	var numDepartments int
	fmt.Scanln(&numDepartments)

	// TODO: Ler o nome de cada departamento, a quantidade de membros e os índices dos membros
	// Criar instâncias de Department, adicionar membros por índice e adicionar a components
	for i := 0; i < numDepartments; i++ {
		var deptName string
		var memberCount int
		fmt.Scanln(&deptName)
		fmt.Scanln(&memberCount)
		// TODO: Criar department, ler índices dos membros, adicionar membros, adicionar a components
	}

	// Ler o índice final a consultar
	var queryIndex int
	fmt.Scanln(&queryIndex)

	// TODO: Imprimir o nome do componente e o salário total no formato: [name]: [salary]
	// Exemplo: fmt.Printf("%s: %d\n", name, salary)
}
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