1. O que é o Padrão Decorator

O padrão decorator é um padrão de design estrutural que permite adicionar recursos adicionais a um objeto dinamicamente sem modificar o código do objeto. Isso é alcançado envolvendo o objeto em uma classe decoradora, proporcionando a capacidade de adicionar, modificar ou aprimorar o comportamento do objeto em tempo de execução.

2. Características e Vantagens do Padrão Decorator

As características e vantagens do padrão decorator incluem:

  • Extensão dinâmica da funcionalidade de um objeto sem modificar seu código.
  • Conformidade com o Princípio Aberto-Fechado, permitindo a adição e remoção dinâmica de decoradores.
  • Capacidade de combinar vários decoradores para alcançar extensões de funcionalidade aninhadas.
  • Independência dos decoradores da forma como o objeto é decorado, permitindo que as alterações sejam feitas de forma independente.

3. Exemplos de Aplicações Práticas do Padrão Decorator

O padrão decorator possui diversas aplicações práticas no desenvolvimento de software, tais como:

  • Adição dinâmica de funcionalidade de registro
  • Adição dinâmica de funcionalidade de cache
  • Validação dinâmica de dados

4. Implementação do Padrão Decorator em Golang

4.1. Diagrama de Classe UML

Padrão Decorator em Golang

4.2. Introdução do Exemplo

No exemplo, temos uma interface Component e uma classe ConcreteComponent que implementa o método Operation da interface Component.

Depois temos uma classe Decorator, que também implementa a interface Component. A classe Decorator possui uma variável membro do tipo Component.

Ambas as classes ConcreteDecoratorA e ConcreteDecoratorB herdam da classe Decorator e implementam funcionalidades adicionais sobrescrevendo o método Operation.

4.3. Passo 1 da Implementação: Definir a interface e a classe de implementação

type Component interface {
	Operation() string
}

type ConcreteComponent struct {}

func (c *ConcreteComponent) Operation() string {
	return "operação específica do componente"
}

4.4. Passo 2 da Implementação: Definir o decorador

type Decorator struct {
	component Component
}

func (d *Decorator) Operation() string {
	return d.component.Operation()
}

4.5. Passo 3 da Implementação: Implementação do decorador

type ConcreteDecoratorA struct {
	Decorator
	addedState string
}

func (c *ConcreteDecoratorA) Operation() string {
	c.addedState = "Novo Estado"
	return c.addedState + " " + c.component.Operation()
}

type ConcreteDecoratorB struct {
	Decorator
}

func (c *ConcreteDecoratorB) Operation() string {
	return "operação específica do decorador B " + c.component.Operation()
}

4.6. Passo 4 da Implementação: Utilizando o decorador

func main() {
	component := &ConcreteComponent{}

	decoratorA := &ConcreteDecoratorA{}
	decoratorA.component = component

	decoratorB := &ConcreteDecoratorB{}
	decoratorB.component = decoratorA

	result := decoratorB.Operation()
	fmt.Println(result)
}

5. Comparação do Padrão Decorator com Outros Padrões de Design

5.1. Comparação com Herança

Comparado com a herança, o padrão decorator pode adicionar funcionalidade dinamicamente sem modificar o código existente, enquanto a herança é estática e precisa ser determinada em tempo de compilação.

5.2. Comparação com o Padrão de Proxy Estático

O padrão decorator e o padrão de proxy estático podem ambos alcançar a extensão de funcionalidades, mas o padrão decorator é mais flexível e permite a adição e remoção dinâmica de funcionalidades.

5.3. Comparação com o Padrão de Proxy Dinâmico

Tanto o padrão decorator quanto o padrão de proxy dinâmico podem estender a funcionalidade de objetos em tempo de execução. No entanto, o padrão decorator decora um único objeto, enquanto o padrão de proxy dinâmico envolve acesso indireto entre o objeto proxy e o objeto de destino.