Não existe uma definição universalmente aceita de padrão, mas talvez o melhor seja começar por Cristopher Alexander, uma inspiração para muitos entusiastas de padrões: "Cada padrão descreve um problema que ocorre repetidamente no nosso ambiente e então descreve a essência da solução desse problema, de tal forma que você possa usar essa solução um milhão de vezes, sem jamais fazê-lo exatamente da mesma forma". Alguns elementos que compõem um padrão são:
Em geral, um padrão tem quatro elementos essenciais, apresentados em:
A interface com o usuário de muitos programas fornecem várias formas de executar um determinado comando. Por exemplo, para recortar um bloco de texto em um editor de textos, podemos selecionar Editar → Recortar a partir do menu, clicar no botão da barra de ferramentas com o ícone de uma tesoura ou simplesmente digitar a combinação de teclas CTRL+X. Isso, naturalmente, é fácil de implementar. Simplesmente associamos os tratadores dos eventos do menu, da barra de ferramentas e do teclado ao código que executa a operação "recortar". Mas um comando desses faz mais do que simplesmente executar a operação desejada. Por exemplo, se não há nada para recortar, então o botão da barra de ferramentas deve ser desabilitado. Um item de menu ou botão desabilitado tem uma aparência visual diferente. Também é útil que o comando "recortar" possa lembrar se está habilitado ou não. Dessa forma, um comando tem tanto um comportamento como um estado. Qual padrão de projeto resolve esse problema?
Objetos compostos normalmente têm estruturas complexas, constituídas de elementos individuais. Alguns elementos podem, por sua vez, ter elementos filhos. Estes elementos podem pertencer a várias classes. Uma operação sobre um elemento visita seus filhos, aplica operações sobre eles e combina os resultados. Um exemplo é um contêiner de interface com o usuário que é constituído por componentes, alguns contendo componentes adicionais. As classes Component
e Container
do pacote java.awt
contêm várias operações tais como getPreferredSize
e repaint
, que são aplicadas recursivamente sobre os elementos filhos. Entretanto, não é fácil acrescentar novas operações neste projeto. Suponha que você deseje suportar uma nova operação para contêineres e componentes de interface com o usuário. Essa operação teria de ser acrescentada na classe Component
e em várias subclasses. Só que o programador de aplicação não pode acrescentar métodos em classes de bibliotecas. Qual padrão de projeto ensina como o projetista de uma biblioteca pode fornecer um mecanismo extensível para resolver esse problema:
Quando uma abstração pode ter uma entre várias implementações possíveis, a maneira usual de acomodá-las é usando a herança. Uma classe abstrata define a interface para a abstração, e subclasses concretas a implementam de formas diferentes. Mas essa abordagem nem sempre é suficientemente flexível. A herança liga uma implementação à abstração permanentemente, o que torna difícil modificar, aumentar e reutilizar abstrações e implementações independentemente.
Considere a implementação de um sistema estatístico que permita ao usuário selecionar o método a ser utilizado para o calculo da média de uma série de números, que podem ser fornecidos pelo usuário por meio de um vetor, uma matriz, uma lista dentre diversas outras formas.
O código a seguir apresenta a forma como o usuário vai utilizar as chamadas aos métodos estatísticos empregados.
Integer[] values = {1, 2, 3, 4, 5};
Element vector = new ElementVector(values, new ArithmeticMean());
System.out.println(vector.getMean());
vector = new ElementVector(values, new HarmonicMean());
System.out.println(vector.getMean());
List<Integer> listValues = new ArrayList<>();
listValues.add(1);
listValues.add(2);
listValues.add(3);
listValues.add(4);
listValues.add(5);
Element list = new ElementList(listValues, new ArithmeticMean());
System.out.println(list.getMean());
list = new ElementList(listValues, new GeometricMean());
System.out.println(list.getMean());
ElementVector
e ArithmeticMean
.Dado o código a seguir:
public class MyThread extends Thread
{
public MyThread()
{
System.out.print("MyThread ");
}
public void run()
{
super.run();
System.out.print("run ");
}
public void start()
{
super.start();
System.out.print("start ");
}
}
public class TestThreads
{
public TestThreads()
{
System.out.print("TestThreads ");
}
public static void main(String[] args)
{
Thread t = new MyThread();
t.start();
}
}
Qual será o resultado impresso após a execução do programa apresentado: