"Simple" Factory (padrão de desenho)

From Wiki**3

Revision as of 12:55, 1 October 2010 by Root (talk | contribs) (As classes das obras)

O padrão "simple" factory fornece uma forma de criar objectos a partir de uma descrição externa.

Exemplo

Neste exemplo, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de uma descrição textual desses objectos.

Este exemplo utiliza informação de tipos em tempo de execução, mas qualquer outro que realizasse a mesma função por outras vias seria admissível.

Introdução

Considere-se o ficheiro obras.txt, contendo descrições de DVDs (título e realizador) e de Livros (título, autor e ISBN):

 DVD:Era uma vez na amadora:Fernando Fonseca
 DVD:Lumiar selvagem:Pedro Fonseca
 Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890
 Livro:Bairro alto e o budismo zen:Zun Tse Fonseca:1234567891
 DVD:48 horas para o exame:Orlando Fonseca
 Livro:Lux e o insucesso escolar - Uma visao matematica:Carlos Alberto Fonseca:1234567892

A ideia da aplicação que se descreve de seguida é a leitura de cada linha como se da criação de um objecto se tratasse. Assim, o primeiro campo (os campos são separados por ":") define a classe do objecto e os restantes campos são passados como argumentos ao constructor da classe. Pretende-se ainda que a implementação seja suficientemente flexível para que resista à utilização de descrições erradas ou de descrições de objectos desconhecidos.

Interfaces

Além dos aspectos acima, cada obra é classificada com uma ou mais interfaces, sendo o seu comportamento definido por elas.

 interface Folheável {
   void folhear();
 }
 interface Legível {
   void ler();
 }
 interface Rodopiável {
   void rodopiar();
 }

Estas interfaces não são realmente utilizadas para os aspectos de criação dos objectos, mas permitem ilustrar a utilização de código específico em conjunto com código genérico.

A classe de base

A classe de base de todas as entidades a criar é Obra. Esta classe, por um lado, impõe às suas subclasses a definição do método processa e, por outro, recorrendo a uma fábrica simples que, fazendo uso de informação de tipos em tempo de execução, permite criar instâncias das suas subclasses, de acordo com uma descrição passada como argumento (cria).

<java5>

import java.util.ArrayList;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public abstract class Obra {
  static { System.out.println("Carregamento da classe Obra"); }
  public abstract void processa();
  static Obra cria(String dsc) {
    String dados[] = dsc.split(":");
    try {
      ArrayList<String> ctorargs = new ArrayList<String>(dados.length-1);
      Class tipo = Class.forName(dados[0]);
      for (int ix = 1; ix < dados.length; ix++)
        ctorargs.add(ix-1, dados[ix]);
      Constructor ctor = tipo.getConstructors()[0];  // hack? só existe um...
      return (Obra)ctor.newInstance(ctorargs.toArray());
    }
    catch (ClassNotFoundException e) {  // forName
      System.err.println("!! TIPO DE OBRA DESCONHECIDO !!");
      System.err.println(e);
      return null;
    }
    catch (InstantiationException e) {  // newInstance
      System.err.println("!! TIPO DE OBRA ABSTRACTO !!");
      System.err.println(e);
      return null;
    }
    catch (IllegalAccessException e) {  // newInstance
      System.err.println("!! TIPO DE OBRA SEM CONSTRUCTOR ACESSÍVEL!!");
      System.err.println(e);
      return null;
    }
    catch (IllegalArgumentException e) {  // newInstance
      System.err.println("!! TIPO DE OBRA MAL DESCRITO !!");
      System.err.println(e);
      return null;
    }
    catch (InvocationTargetException e) {  // newInstance
      System.err.println("!! TIPO DE OBRA COM CONSTRUCTOR EM APUROS !!");
      System.err.println(e);
      return null;
    }
  }
}

</java5>

Note-se o tratamento de várias excepções, em particular, o tratamento da excepção ClassNotFoundException, que tem, neste contexto especial, um significado para a aplicação algo distinto do habitual.

Note-se ainda o tratamento de InvocationTargetException, que permite lidar com as excepções específicas do constructor da obra em causa (exception chaining).

As classes das obras

Para DVDs: <java5>

public class DVD extends Obra implements Rodopiável, Legível {
  static { System.out.println("Carregamento da classe DVD"); }
  String _título;
  String _realizador;
  public DVD(String título, String realizador) {
    _título     = título;
    _realizador = realizador;
  }
  public String toString() { return "DVD:" + _título + ":" + _realizador; }
  public void ler()      { System.out.println("LER "      + this); }
  public void rodopiar() { System.out.println("RODOPIAR " + this); }
  public void processa() { rodopiar(); ler(); }
}

</java5>

Para livros: <java5>

public class Livro extends Obra implements Folheável, Legível {
  static { System.out.println("Carregamento da classe Livro"); }
  String _título;
  String _autor;
  String _isbn;
  public Livro(String título, String autor, String isbn) {
    _título = título;
    _autor  = autor;
    _isbn   = isbn;
  }
  public String toString() { return "Livro:" + _título + ":" + _autor + ":" + _isbn; }
  public void ler()     { System.out.println("LER "     + this); }
  public void folhear() { System.out.println("FOLHEAR " + this); }
  public void processa() { folhear(); ler(); }
}

</java5>

Aplicação exemplo

Esta aplicação lÃ?ª o ficheiro de obras (propriedade obras) e processa cada linha, criando os objectos correspondentes. Depois de lidas, as obras são processadas (uniformemente).

 import java.util.ArrayList;
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
 
 public class Teste {
   public static void main(String[] args) throws IOException {
     String entrada = System.getProperty("obras", "obras.txt");
     BufferedReader r = new BufferedReader(new FileReader(entrada));
     String linha;
     ArrayList<Obra> obras = new ArrayList<Obra>();
     while ((linha = r.readLine()) != null) {
       Obra o = Obra.cria(linha);
       obras.add(o);
       System.out.println(o);
     }
     r.close();
     System.out.println("****************");
     for (Obra o: obras) o.processa();
   }
 }

A saída da aplicação de teste é a que se apresenta. Note-se a ordem de carregamento das classes.

 % java -Dobras=obras.txt Teste
 Carregamento da classe Obra
 Carregamento da classe DVD
 DVD:Era uma vez na amadora:Fernando Fonseca
 DVD:Lumiar selvagem:Pedro Fonseca
 Carregamento da classe Livro
 Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890
 Livro:Bairro alto e o budismo zen:Zun Tse Fonseca:1234567891
 DVD:48 horas para o exame:Orlando Fonseca
 Livro:Lux e o insucesso escolar - Uma visao matematica:Carlos Alberto Fonseca:1234567892
 ****************
 RODOPIAR DVD:Era uma vez na amadora:Fernando Fonseca
 LER DVD:Era uma vez na amadora:Fernando Fonseca
 RODOPIAR DVD:Lumiar selvagem:Pedro Fonseca
 LER DVD:Lumiar selvagem:Pedro Fonseca
 FOLHEAR Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890
 LER Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890
 FOLHEAR Livro:Bairro alto e o budismo zen:Zun Tse Fonseca:1234567891
 LER Livro:Bairro alto e o budismo zen:Zun Tse Fonseca:1234567891
 RODOPIAR DVD:48 horas para o exame:Orlando Fonseca
 LER DVD:48 horas para o exame:Orlando Fonseca
 FOLHEAR Livro:Lux e o insucesso escolar - Uma visao matematica:Carlos Alberto Fonseca:1234567892
 LER Livro:Lux e o insucesso escolar - Uma visao matematica:Carlos Alberto Fonseca:1234567892