[Expand] Programação com Objectos |
---|
O padrão "simple" factory fornece uma forma de criar objectos a partir de uma descrição externa.
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.
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.
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 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).
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>
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). <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 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