Já revisamos o uso de um objeto singleton, mas talvez você ainda não tenha percebido que essa estratégia é um padrão de projeto e um dos mais usados.
Na verdade, existem muitos desses padrões e eles podem ser classificados de acordo com sua finalidade específica.
Classificação de padrões
Tipo de Padrão | Aplicativo |
---|---|
criacional | Um tipo que resolve o problema de criação de objetos |
Estrutural | Padrões que nos permitem construir uma hierarquia de classes correta e extensível em nossa arquitetura |
Comportamental | Esse agrupamento de padrões facilita a interação segura e conveniente entre objetos em um programa. |
Normalmente, um padrão é caracterizado pelo problema que resolve. Vamos dar uma olhada em alguns padrões que encontramos com mais frequência ao trabalhar com Java:
Padrão | Propósito |
---|---|
solteiro | Já estamos familiarizados com esse padrão — nós o usamos para criar e acessar uma classe que não pode ter mais de uma instância. |
Iterador | Também estamos familiarizados com este. Sabemos que esse padrão nos permite iterar sobre um objeto de coleção sem revelar sua representação interna. É usado com coleções. |
Adaptador | Esse padrão conecta objetos incompatíveis para que possam trabalhar juntos. Acho que o nome do padrão do adaptador ajuda você a imaginar exatamente o que ele faz. Aqui está um exemplo simples da vida real: um adaptador USB para uma tomada de parede. |
Método de modelo |
Um padrão de programação comportamental que resolve o problema de integração e permite alterar etapas algorítmicas sem alterar a estrutura de um algoritmo. Imagine que temos um algoritmo de montagem de carros na forma de uma sequência de etapas de montagem: Chassi -> Carroceria -> Motor -> Interior da Cabine Se colocarmos uma estrutura reforçada, um motor mais potente ou um interior com iluminação adicional, não precisamos alterar o algoritmo e a sequência abstrata permanece a mesma. |
Decorador | Esse padrão cria wrappers para objetos para fornecer funcionalidades úteis. Vamos considerá-lo como parte deste artigo. |
Em Java.io, as seguintes classes implementam padrões:
Padrão | Onde é usado em java.io |
---|---|
Adaptador |
|
Método de modelo | |
Decorador |
padrão decorador
Vamos imaginar que estamos descrevendo um modelo para o projeto de uma casa.
Em geral, a abordagem é assim:
Inicialmente, temos uma escolha de vários tipos de casas. A configuração mínima é de um andar com telhado. Em seguida, usamos todos os tipos de decoradores para alterar parâmetros adicionais, o que afeta naturalmente o preço da casa.
Criamos uma classe abstrata House:
public abstract class House {
String info;
public String getInfo() {
return info;
}
public abstract int getPrice();
}
Aqui temos 2 métodos:
- getInfo() retorna informações sobre o nome e características de nossa casa;
- getPrice() retorna o preço da configuração atual da casa.
Também temos implementações padrão da Casa — tijolo e madeira:
public class BrickHouse extends House {
public BrickHouse() {
info = "Brick House";
}
@Override
public int getPrice() {
return 20_000;
}
}
public class WoodenHouse extends House {
public WoodenHouse() {
info = "Wooden House";
}
@Override
public int getPrice() {
return 25_000;
}
}
Ambas as classes herdam a classe House e substituem seu método de preço, definindo um preço personalizado para uma casa padrão. Definimos o nome no construtor.
Em seguida, precisamos escrever classes de decorador. Essas classes também herdarão a classe House . Para fazer isso, criamos uma classe de decorador abstrato.
É aí que colocaremos a lógica adicional para alterar um objeto. Inicialmente, não haverá lógica adicional e a classe abstrata estará vazia.
abstract class HouseDecorator extends House {
}
Em seguida, criamos implementações de decorador. Vamos criar várias classes que nos permitem adicionar recursos adicionais à casa:
public class SecondFloor extends HouseDecorator {
House house;
public SecondFloor(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 20_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + second floor";
}
}
Um decorador que adiciona um segundo andar à nossa casa |
O construtor decorador aceita uma casa que iremos "decorar", ou seja, adicionar modificações. E sobrescrevemos os métodos getPrice() e getInfo() , retornando informações sobre a nova casa atualizada com base na antiga.
public class Garage extends HouseDecorator {
House house;
public Garage(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 5_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + garage";
}
}
Um decorador que adiciona uma garagem à nossa casa |
Agora podemos atualizar nossa casa com decoradores. Para fazer isso, precisamos criar uma casa:
House brickHouse = new BrickHouse();
A seguir, definimos nossocasavariável igual a um novo decorador, passando em nossa casa:
brickHouse = new SecondFloor(brickHouse);
Nossocasavariável agora é uma casa com um segundo andar.
Vejamos os casos de uso envolvendo decoradores:
código de exemplo | Saída |
---|---|
|
casa de tijolos 20000 |
|
Casa de tijolos + segundo andar 40000 |
|
Casa de tijolos + segundo andar + garagem 45000 |
|
Casa de madeira + garagem + segundo andar 50000 |
|
casa de madeira 25000 casa de madeira + garagem 30000 |
Este exemplo ilustra o benefício de atualizar um objeto com um decorador. Então não mudamos ocasa de madeiraobjeto em si, mas em vez disso criou um novo objeto baseado no antigo. Aqui podemos ver que as vantagens vêm com desvantagens: criamos um novo objeto na memória a cada vez, aumentando o consumo de memória.
Veja este diagrama UML do nosso programa:
Um decorador tem uma implementação super simples e altera os objetos dinamicamente, atualizando-os. Os decoradores podem ser reconhecidos por seus construtores, que recebem como parâmetros objetos do mesmo tipo abstrato ou interface da classe atual. Em Java, esse padrão é amplamente utilizado em classes de E/S.
Por exemplo, como já observamos, todas as subclasses de java.io.InputStream , OutputStream , Reader e Writer possuem um construtor que aceita objetos das mesmas classes.
GO TO FULL VERSION