Decorator

Objetivo

Permite adicionar funcionalidade a uma classe já existente sem modificar a classe [1]

Propósito

Muitas vezes gostaríamos de utilizar um método em um certo contrato, mas esse contrato não tem esse método. Se tivermos acesso ao código da classe podemos modificá-lo e adicionar o método que precisamos, mas se não tivermos acesso a essa classe, como fazer ?

A resposta é usar o padrão Decorator, também conhecido como Wrapper. O padrão Decorator cria uma nova classe que tem o mesmo contrato que a classe original, mas adiciona mais métodos que não existem na classe original. Todos os métodos originais são delegados para o original, e novos são construídos usando métodos originais.

Implementação

Digamos que temos uma classe Person com os atributos de nome e sobre nome:

public class Person {

	private String firstName;
	private String lastName;

	// getter e setters
	...
}

Em certo pontos do código precisamos do nome completo então somos forçados a fazer isto:

Person person = ... // obtido de alguma forma

String fullName = person.getFirstName() + " " + person.getLastName();

Gostaríamos de ter um método no objeto que realizasse esta operação, mas não controlamos a classe Person. O que podemos fazer é criar um decorador.

public class PersonDecorator {

	private final Person original;

	public PersonDecorator(Person original){
		this.original = original;
	}

	public String getFirstName(){
		return original.getFirstName();
	}

	public String getLastName(){
		return original.getLastName();
	}

	public String getFullName(){
		return original.getFirstName() + " " + original.getLastName();
	}
}

E agora podemos usar como

Person person = ... // obtido de alguma forma

PersonDecorator decorator = new PersonDecorator(person);

String fullName = decorator.getFullName();

Podemos ir mais longe se a classe Person não for final, e seus métodos forem final, ou se estamos lidando com uma interfaces. Nesse caso podemos usar herança assim:

public class PersonDecorator extends Person {

	private final Person original;

	public PersonDecorator(Person original){
		this.original = original;
	}

	@Override
	public String getFirstName(){
		return original.getFirstName();
	}

	@Override
	public String getLastName(){
		return original.getLastName();
	}

	public String getFullName(){
		return original.getFirstName() + " " + original.getLastName();
	}
}

Discussão

Utilizar decoradores para objetos de dados não é muito útil na prática. É mais útil para adicionarmos métodos em objetos com interfaces como Service. O uso mais corriqueiro de decoradores depende em larga escala das capacidades da linguagem. A linguagem Java não suporta estes conceitos de forma simples e acabamos construindo a funcionalidade usando métodos estáticos em vez de objetos.

Outras linguagens têm capacidades que permite construir decoradores mais facilmente. A forma mais usada para isso são Extention Methods.

Extention Methods

Extention Methods ou métodos de extensão são métodos estáticos que podem ser invocados como métodos de instancia comuns cabendo ao compilador fazer as respetivas modificações.

Métodos de extensão estão disponíveis em linguagens como C# e Kotlin, por exemplo.

Em C#, os métodos de extensão são métodos estáticos cujo parâmetro marcado com this representa o objeto original. Podemos escrever:

// C#

public static class PersonExtentions {

	public static String fullName(this Person original) {
		return original.FirstName + " " + original.LastName;
	}
}

e usar assim :

// C#
Person person = ... // obtido de alguma forma

String fullName = person.fullName();

Repare que o conteúdo do método é o mesmo que no exemplo em java, mas na hora de usar não precisamos criar um novo objeto. O compilador entende aquela forma de chamar e acba convertendo a chamado para algo como:

// C#
Person person = ... // obtido de alguma forma

String fullName = PersonExtentions.fullName(person);

Que seria o que escreveríamos em Java.

Padrões associados

O padrão Decorator está relacionado aos padrões Adapter e Proxy devido à semelhança de implementação entre os três. Embora os objetivos sejam completamente diferentes, as implementações são muito semelhantes.

A tabela a seguir tentar resumir as relações

Mapeia Contratos

Altera Contrato

Adiciona lógica

Adapter

x

Decorator

x

x

Proxy

x

x

O padrão Decorator também é frequentemente utilizado para expandir a funcionalidade de objetos construídos no padrão Service.

Bibliografia

[1] Ralph Johnson, Erich Gamma, John Vlissides, Richard Helm Design Patterns: Elements of Reusable Object-Oriented Software: Addison-Wesley (2005)

Scroll to Top