Imagine que você tem um sistema de notificações. A notificação básica envia mensagens via e-mail. No entanto, você deseja adicionar a capacidade de enviar mensagens via SMS e registrar as notificações em um log. Usar o Decorator permite adicionar essas funcionalidades de forma flexível e modular.
Modificar diretamente a classe base que possui a funcionalidade pode ser uma má ideia, especialmente se várias funcionalidades forem opcionais e não aplicáveis em todas as situações.
Decorator resolve esse problema, permitindo que comportamentos adicionais sejam anexados dinamicamente a um objeto existente sem alterar sua estrutura original.
//1
interface INotifier {
void send(String message);
}
- Interface comum para o componente e o decorador
//1
class EmailNotifier implements INotifier {
@Override
public void send(String message) {
System.out.println("Enviando e-mail📨: " + message);
}
}
- Componente concreto que implementa o comportamento básico
//1
abstract class NotifierDecorator implements INotifier {
protected INotifier wrappedNotifier;
public NotifierDecorator(INotifier INotifier) {
this.wrappedNotifier = INotifier;
}
@Override
public void send(String message) {
wrappedNotifier.send(message);
}
}
- Classe Decorator que adiciona funcionalidades ao INotifier
//1
class SMSNotifierDecorator extends NotifierDecorator {
public SMSNotifierDecorator(INotifier INotifier) {
super(INotifier);
}
@Override
public void send(String message) {
super.send(message);
sendSMS(message);
}
private void sendSMS(String message) {
System.out.println("Enviando SMS📲: " + message);
}
}
- Decorador concreto que adiciona a funcionalidade de enviar SMS
//1
class LogNotifierDecorator extends NotifierDecorator {
public LogNotifierDecorator(INotifier INotifier) {
super(INotifier);
}
@Override
public void send(String message) {
super.send(message);
logNotification(message);
}
private void logNotification(String message) {
System.out.println("Registrando notificação: " + message);
}
}
- Decorador concreto que adiciona a funcionalidade de logar notificações
Aplicação do Decorator
public class Application {
public static void main(String[] args) {
//1
INotifier emailNotifier = new EmailNotifier();
//2
INotifier smsNotifier = new SMSNotifierDecorator(emailNotifier);
//3
INotifier logAndSmsNotifier = new LogNotifierDecorator(smsNotifier);
//4
logAndSmsNotifier.send("Olá, esse é um exemplo de notificação!");
}
}
- Notificador básico
- Adiciona a funcionalidade de SMS
- Adiciona a funcionalidade de log, além do SMS
- Envia a notificação
- A interface
INotifier
define o métodosend()
, que todas as classes concretas e decoradores irão implementar. - A classe
EmailNotifier
é o componente concreto, implementando o envio de notificações via e-mail. - As classes
SMSNotifierDecorator
eLogNotifierDecorator
são decoradores concretos que adicionam funcionalidades de envio de SMS e registro de logs, respectivamente. Elas utilizam a composição para adicionar novos comportamentos de forma incremental.
Decorator é útil quando:
- Quando você deseja adicionar responsabilidades ou funcionalidades adicionais a objetos individuais de maneira dinâmica.
- Quando a extensão por herança não é viável ou cria uma complexidade excessiva.
- Quando diferentes combinações de funcionalidades devem ser aplicadas a objetos, e criar subclasses para todas as combinações seria impraticável.
- Java I/O: Biblioteca Java é o sistema de classes de Input/Output (I/O). As classes de I/O utilizam Decorators para adicionar funcionalidades como buffering, compressão ou criptografia a streams de dados. Abstração (Componente):
InputStream
,OutputStream
. Componente Concreto:FileInputStream
,FileOutputStream
. Decoradores:BufferedInputStream
,DataInputStream
,GZIPInputStream
. - Spring Security: Adicionar diferentes funcionalidades de segurança, como autenticação, autorização, e controle de acesso. Isso é feito por meio de
AuthenticationProvider
eSecurityContext
, que são decoradores sobre a autenticação básica. Para mais sobre. - Servlet API: Para modificar ou estender a funcionalidade de requisições e respostas HTTP. Isso é feito por meio de
HttpServletRequestWrapper
eHttpServletResponseWrapper
, que permitem "decorar" uma requisição ou resposta HTTP com funcionalidades adicionais(cabeçalhos extras, manipular parâmetros, etc).
Em resumo, é ideal em cenários onde a funcionalidade deve ser incrementada de forma flexível e dinâmica, como em sistemas de notificação, streams de I/O ou interfaces gráficas.