terça-feira, 14 de agosto de 2012

Utilizando Template Method em Java


O que é Template Method?
Desenvolvimento OO atual consiste na criação de sistema complexos onde o simples polimorfismo não resolve com coerência os problemas encontrados. Por isso existem os Design Patterns (padrões de projeto) que consistem em apresentar várias técnicas de desenvolvimento para determinados problemas. Template Method é um desses padrões. Vide mais informações sobre GoF em:
http://en.wikipedia.org/wiki/Design_Patterns

Onde utilizar um Template Method?
Imagine a seguinte situação: precisamos definir um esqueleto de algorítimo que não pode ser modificado, porém que é constituido de etapas nas quais devem variar de caso a caso. Este problema é resolvido por um template method. Veja o código, sem o template method, abaixo:

interface IMyExample {
  public void operationOne();
  public void operationTwo();
  public int calculate();
}

class MyExample implements IMyExample {

  protected int number;

  public MyExample() { number = 0; }

  @Override
  public void operationOne() { number++; }

  @Override
  public void operationTwo() { number--; }

  @Override
  public int calculate() {
    operationOne();
    operationTwo();
    return number;
  }
}

class ExampleA extends MyExample {
  @Override
  public void operationOne() {
    number += 2;
  }
}

class ExampleB extends MyExample {
  @Override
  public void operationTwo() {
    number -= 2;
  }
}

class TestDriver {
  public static void main(String args[]) {
    ExampleA a = new ExampleA();
    ExampleB b = new ExampleB();
    System.out.println( "A: " + a.calculate() );
    System.out.println( "B: " + b.calculate() );
  }
}

Este é um caso típico de objetos polimórficos onde cada um se comporta da maneira esperada. Onde este exemplo apresenta falhas?
  1. Nossa assinatura é definida por uma interface e por consequência, os métodos são públicos. Porém, em nosso projeto, os métodos operationOne() e operationTwo() definem comportamentos que não poderiam ser acessados externamente. O encapsulamento fica prejudicado.
  2. Nosso método calculate() também poderia ser sobrescrito. Isso poderia ser facilmente resolvido, mas o design pattern também vai garantir esse comportamento.

Resolvendo o mesmo problema com Template Method
Vamos agora ao mesmo problema com a aplicação do padrão. Suponha que o método operationOne() deve ser obrigatoriamente sobrescrito. Já o método operationTwo() pode ser sobrescrito, mas opcionalmente.

abstract class MyExample {
  protected int number;

  public MyExample() { number = 0; }

  // Método que precisa ser sobrescrito
  protected abstract void doOperationOne();

  // Método que pode ser sobrescrito
  protected void operationTwoHook() { number--; }

  // Template Method
  public final int calculate() {
    doOperationOne();
    operationTwoHook();
    return number;
  }
}

class ExampleA extends MyExample {

  @Override
  protected void doOperationOne() {
    number++;
  }

}

class ExampleB extends MyExample {

  @Override
  protected void doOperationOne() {
    number += 2;
  }

  @Override
  protected void operationTwoHook() {
    number--;
  }

}

class TestDriver {
  public static void main(String args[]) {
    ExampleA a = new ExampleA();
    ExampleB b = new ExampleB();
    System.out.println( "A: " + a.calculate() );
    System.out.println( "B: " + b.calculate() );
  }
}

Algumas vantagens são encontradas neste padrão:
  1. Observe que doOperationOne() é abtract e portanto existe a necessidade de sobrescrever
  2. A notação do no início do método é uma forma de representar que o método precisa ser sobrescrito (use esta notação se quiser!)
  3. O método operationTwoHook() já possui um comportamento padrão, porém, pode ou não ser sobrescrito
  4. A notação Hook no final do método indica que a sobrescrição do mesmo é opcional
  5. O encapsulamento foi garantido, pois ambos os métodos são protected
  6. O método validate() é nosso template method, ou seja, o esqueleto do algoritmo que em hipótese nenhuma pode ser modificado. Por isso ele é final

Esse foi um simples exemplo que compreende o padrão template method. Existem vários casos onde o mesmo poderá ser aplicado. Uma situação de login de usuário no sistema, por exemplo. O ato de login é sempre o mesmo, porem algumas de suas etapas podem variar de sistema para sistema. Você poderá construir uma biblioteca genérica de login trabalhando com esse padrão.

"Não acordo entre Homens e Leões" Aquiles
Bons estudos!
Guilherme Pontes

Nenhum comentário:

Postar um comentário