
Padrão de projeto Decorator
Alguns dias atrás, eu estava trabalhando em um sistema responsável por sincronizar dados entre dois sistemas. A nova tarefa exigia reutilizar esse mesmo sistema para sincronizar com um terceiro, mas com alguns extras: eu queria enviar uma notificação no início, outra no fim, adicionar logs e aceitar mais parâmetros.
O problema? Esse método já era usado em vários pontos do sistema. Alterá-lo diretamente tornaria o código frágil e com alto risco de quebrar outras partes.
Foi aí que me lembrei de uma aula sobre Design Patterns, onde o professor explicou o padrão Decorator.
Na hora, tudo fez sentido: eu poderia “decorar” minha função com novos comportamentos — sem tocar no código original!
O que é o padrão Decorator?
O padrão Decorator permite adicionar funcionalidades extras a um objeto de forma dinâmica — sem precisar alterar a estrutura da classe original.
Imagine que você tem uma xícara de café. Às vezes você quer café puro, às vezes com leite, chocolate, chantilly… ou com tudo isso junto!
Você não precisa criar uma classe para cada combinação possível — basta ir “decorando” a base com os complementos desejados.
Para ilustrar melhor, imagine este diagrama:

Ainda parece confuso? Calma, vou usar um exemplo mais simples — e tenho certeza de que você vai sair daqui entendendo tudo!
Exemplo prático com café
Você começa com um CafePuro
(que dev que não gosta de uma bom café para programar? :D), e pode adicionar Leite
, Açucar
, Chantilly
e ir, combinando da forma que quiser.
Vamos começar com um CafePuro
. Depois, podemos adicionar Leite
, Açúcar
, Chantilly
… e ir combinando como quiser.
Vamos criar a interface do nosso componente, chamada Cafe
.
Ela define os métodos que retornam a descrição e o preço do café:
interface Cafe
{
public function getDescricao(): string;
public function getPreco(): float;
}
Agora criamos a classe CafePuro
, que implementa a interface Cafe
:
class CafePuro implements Cafe
{
public function getDescricao(): string
{
return "Café puro";
}
public function getpreco(): float
{
return 0.5;
}
}
Em seguida, criamos a classe CafeDecorator
, que será a base para todos os nossos decoradores:
class CafeDecorator implements Cafe
{
public function __construct(private readonly Cafe $cafe)
{
}
public function getDescricao(): string
{
return $this->cafe->getdescricao();
}
public function getPreco(): float
{
return $this->cafe->getPreco();
}
}
Agora, os nossos decoradores (Leite
, Açucar
, Chantilly
) vão estender CafeDecorator
e sobrescrever os métodos:
Classe Leite:
class Leite extends CafeDecorator
{
public function __construct(private Cafe $decorator)
{
}
#[Override]
public function getDescricao(): string
{
return $this->decorator->getDescricao() .' + Leite';
}
#[Override]
public function getPreco(): float
{
return $this->decorator->getPreco() + 1.30;
}
}
Classe Chantillty:
class Chantily extends CafeDecorator
{
public function __construct(private Cafe $decorator)
{
}
#[Override]
public function getDescricao(): string
{
return $this->decorator->getDescricao() .' + Chantily';
}
#[Override]
public function getPreco(): float
{
return $this->decorator->getPreco() + 1.00;
}
}
Classe Açucar
class Acucar extends CafeDecorator
{
public function __construct(private Cafe $decorator)
{
}
#[Override]
public function getDescricao(): string
{
return $this->decorator->getDescricao() .' + Açucar';
}
#[Override]
public function getPreco(): float
{
return $this->decorator->getPreco() + 2.00;
}
}
Com as classes prontas, vamos ao código cliente — ou seja, como usamos esses decorators na prática.
Começamos com um café puro:
Para ficar mais didático, primeiro vamos “fazer” o café puro:
$cafe = new CafePuro();
echo $cafe->getDescricao(). "\n"; //Café Puro
echo $cafe->getPreco(). "\n"; //0.5
Agora queremos adicionar leite ao café. Para isso, basta passar a instância anterior para o construtor da classe Leite
:
$cafe = new \Dtgfranca\Decorator\Leite($cafe);
echo $cafe->getDescricao(). "\n"; //Saída: Café Puro + Leite
echo $cafe->getPreco(). "\n"; //Saída: 1.8
Em seguida, adicionamos açúcar:
//Adiciona o açucar
$cafe = new \Dtgfranca\Decorator\Acucar($cafe);
echo $cafe->getDescricao(). "\n";//Saída: Café Puro + Leite + Açucar
echo $cafe->getPreco(). "\n"; //Saída: 3.8
E por fim, o chantilly:
//Adiciona o chantily
$cafe = new \Dtgfranca\Decorator\Chantily($cafe);
echo $cafe->getDescricao(). "\n"; //Saída: Café Puro + Leite + Açucar + Chanitly
echo $cafe->getPreco(). "\n";//Saída: 3.8
Repare que conseguimos adicionar novos comportamentos sem modificar a classe original (CafePuro
). Esse é exatamente o espírito do princípio Open/Closed do SOLID:
“Aberto para extensão, fechado para modificação.”
Quando queremos adicionar algo novo, não alteramos o que já existe — apenas estendemos com um decorator.
Outro princípio aplicado aqui é o Single Responsibility (SRP). Cada classe tem um único motivo para mudar. Por exemplo: CafePuro
só mudaria se o preço do café ou sua descrição mudassem. O comportamento extra (leite, açúcar, etc.) está isolado nos decoradores.
Essa abordagem pode parecer simples, mas é muito poderosa.
Em projetos legados, onde mexer em código antigo pode gerar bugs inesperados, usar o padrão Decorator é uma maneira elegante e segura de adicionar comportamentos extras.
O código completo está disponível no GitHub, caso queira testar e brincar com outras combinações:

Como criar e publicar seu próprio pacote PHP no Packagist
Olá pessoal! Hoje quero compartilhar com vocês como criar e publicar pacotes PHP no Packagist. Você já criou um código PHP reutilizável e queria instalar em outro projeto sem a necessidade de ficar duplicando código e até compartilhar com a comunidade PHP?
Continue reading…
Pare de usar env() dentro da sua aplicação Laravel (fora da config)
Venho compartilhar com vocês uma dica rápida sobre o Laravel. Vejo muitas pessoas enfrentando problemas ao tentar acessar variáveis de ambiente dentro da aplicação. O erro mais comum acontece quando os desenvolvedores utilizam a função env()
diretamente em suas classes ou serviços, para ler configurações do arquivo .env
, e depois não entendem por que, em alguns momentos, essa função retorna null
.

Instalando o Apache Kafka
Fala, pessoal! Hoje gostaria de compartilhar com vocês um pouco sobre o Apache Kafka. Tenho trabalhado com o Kafka para fazer uma sincronização com vários bancos de dados e achei interessante compartilhar um pouco do que venho aprendendo. Além disso, este artigo será uma forma de ter um local de fácil acesso caso eu tenha alguma dúvida no futuro.

Criando um servidor Git localmente
Olá pessoal!
Hoje gostaria de compartilhar uma dica rápida e útil: como criar um servidor Git localmente. Imagine a seguinte situação: você e seu colega estão desenvolvendo uma feature importante no escritório e precisam entregá-la em poucos dias. De repente, a internet cai e seus dados móveis não funcionam. Você precisa urgentemente enviar para seu colega a parte que acabou de desenvolver. Conseguem sentir o frio na barriga?
Sabiam que é possível ter um servidor Git local onde você pode clonar e fazer push sem precisar de internet, apenas usando a rede interna? É exatamente isso que gostaria de mostrar hoje. Vamos lá!
Continue reading…
Como instalar várias versões do PHP no ubuntu
Falaaa Galera! Novamente com mais um tutorial super rápido e prático de como configurar várias versões do PHP no Ubuntu. Esperem que gostem.
Continue reading…
Instalando o certificado ssl no apache
Fala pessoal!! Hoje, vou ensinar como criar certificados apache auto assinado. Isso é muito importante quando estamos criando nossos sistemas web e queremos adicionar um certificado ssl no nosso servidor web. Nesse tutorial, estou utilizando Ubuntu 22.04.
Continue reading…
Desbravando testes unitários
A idéia de escrever esse post surgiu após eu fazer uma apresentação sobre testes unitários na empresa que trabalho. Durante a apresentação, fiz um live code em que criei um endpoint na qual utilizei os conceitos, que irei abordar, para fazermos a isolação do componente. Gostaria muito de fazer uma aplicação aqui no blog, mas pode ser que fique muito tedioso fazer um passo a passo. Então, decidi fazer um resumo do que foi apresentado, com exemplos de código para melhor entendimento.
Continue reading…
Modelo de maturidade de Richardson – Mensurando a maturidade da sua api
Fala, pessoal! Hoje tenho algo interessante sobre api restful. Quando você está criando uma api restful já parou para pensar se o que estamos desenvolvendo está correto e se existe algum padrão a ser seguido ao criar uma api restful?
Continue reading…
Criando aplicação escalável — Parte 2
Fala pessoal! Tudo bem com vocês? Seguindo a nossa jornada da criação de uma aplicação escalável, nessa segunda parte venho contar para vocês como foi o início dos testes, para quem não leu a primeira parte clique aqui.
Continue reading…