"Simple" Factory (padrão de desenho): Difference between revisions

From Wiki**3

 
(25 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{TOCright}}O padrão "simple" factory fornece uma forma de criar objectos a partir de uma descrição externa.
#REDIRECT [[ist:"Simple" Factory (padrão de desenho)]]
 
== 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 (Java)|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 <tt>obras.txt</tt>, contendo descrições de DVDs (título e realizador) e de Livros (título, autor e [[wikipedia:ISBN|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 "<tt>:</tt>") 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.
 
<java5>
interface Folheável {
  void folhear();
}
</java5>
 
<java5>
interface Legível {
  void ler();
}
</java5>
 
<java5>
interface Rodopiável {
  void rodopiar();
}
</java5>
 
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 é <tt>Obra</tt>. Esta classe, por um lado, impõe às suas subclasses a definição do método <tt>processa</tt> 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 (<tt>cria</tt>).
 
<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 <tt>ClassNotFoundException</tt>, que tem, neste contexto especial, um significado para a aplicação algo distinto do habitual.
 
Note-se ainda o tratamento de <tt>InvocationTargetException</tt>, 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 <tt>obras</tt>) e processa cada linha, criando os objectos correspondentes. Depois de lidas, as obras são processadas (uniformemente).
<java5>
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();
  }
}
</java5>
 
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'''
  <font color="green">Carregamento da classe Obra</font>
  <font color="green">Carregamento da classe DVD</font>
  DVD:Era uma vez na amadora:Fernando Fonseca
  DVD:Lumiar selvagem:Pedro Fonseca
  <font color="green">Carregamento da classe Livro</font>
  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
 
[[category:OOP]]

Latest revision as of 14:09, 7 November 2018