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

From Wiki**3

(Registo de ligações na rede social)
 
(35 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
{{NAVPO}}
 
{{NAVPO}}
<!--{{TOCright}}-->
+
{{TOCright}}
 
O padrão "simple" factory fornece uma forma de criar objectos a partir de uma descrição externa.
 
O padrão "simple" factory fornece uma forma de criar objectos a partir de uma descrição externa.
  
== Exemplo ==
+
O processo de criação de objectos a partir de texto é um processo semelhante à compilação e implica interpretar esse texto e construir a semântica.
  
Neste exemplo, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de uma descrição textual desses objectos.
+
== Utilização de Método Independente da Linguagem ==
  
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.
+
O exemplo abaixo apresenta o caso simplificado de uma rede social. A rede social permite manipular as várias entidades, por forma a exercitar as várias interacções entre elas, nomeadamente a interacção de agentes e a publicação de conteúdos ("publicações", na nomenclatura usada no código abaixo).
  
=== Introdução ===
+
=== Ficheiro de inicialização textual ===
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.
+
<source lang="text">
 +
PERSON|1|Luke Skywalker|wimp@resistance.org|123-456-789
 +
PERSON|2|Darth Vader|vader@imperial.net|911
 +
PERSON|3|Master Yoda|yoda@jedi.edu|1
 +
PERSON|4|Montgomery Scott|scotty@starfleet.net|987-654-321
 +
PERSON|5|James T. Kirk|kirk@starfleet.net|987-654-321
 +
PERSON|6|Jean-Luc Picard|picard@starfleet.net|987-123-654
 +
ORGANIZATION|7|Google|noreply@google.com|0
 +
ORGANIZATION|8|Melkor & Sauron’s|recruiting@utumno.com|999
 +
ORGANIZATION|9|Starfleet|info@starfleet.net|999
 +
MESSAGE|10|1|3|cities|look at this http://bit.ly/p0XoDW
 +
MESSAGE|11|2|1|we have to talk|Luke, I’m your father!
 +
MESSAGE|12|1|2|Re: we have to talk|Nooooooooooo!!!!
 +
MESSAGE|13|5|4|Teleporter|Beam me up, Scotty!
 +
MESSAGE|14|6|4|Teleporter|Beam me up, Scotty!
 +
NOTE|15|3|Cookies|Mix everything and apply a little Force for 30 seconds
 +
NOTE|16|4|Dilithium crystals|Remember never to put them in the freezer
 +
URI|17|1|Angry Ewoks|http://jedi.edu/ewoks/
 +
URI|18|2|Imperial Bank|http://bank.imperial.net/
 +
IMAGE|19|1|Tatooine|[desert scene with two suns setting]
 +
CONNECTION|1|2|0|1
 +
CONNECTION|1|3|1|1
 +
CONNECTION|4|5|1|1
 +
CONNECTION|4|6|1|1
 +
</source>
  
=== Interfaces ===
+
Os formatos são os seguintes.
 +
<source lang="text">
 +
PERSON|id|name|email|phone
 +
ORGANIZATION|id|name|email|phone
 +
MESSAGE|id|from|to|subject|body
 +
NOTE|id|owner|title|text
 +
IMAGE|id|owner|description|content
 +
URI|id|owner|description|uri
 +
CONNECTION|id1|id2|id1-accepting?|id2-accepting?
 +
</source>
  
Além dos aspectos acima, cada obra é classificada com uma ou mais interfaces, sendo o seu comportamento definido por elas.
+
=== Leitura do ficheiro de texto -- função principal de leitura ===
  
<java5>
+
Função principal de leitura do ficheiro com a informação textual (no projecto, este ficheiro é tipicamente passado ao programa através da propriedade "import").
interface Folheável {
 
  void folhear();
 
}
 
</java5>
 
  
<java5>
+
<source lang="java">
interface Legível {
+
class Network {
  void ler();
+
        //...
}
+
  public void importFile(String name) throws ImportFileException {
</java5>
+
    try (BufferedReader reader = new BufferedReader(new FileReader(name))) {
 +
      String line;
 +
      while ((line = reader.readLine()) != null) {
 +
        String[] fields = line.split("\\|");
 +
        try {
 +
          registerEntry(fields);
 +
        } catch (UnknownDataException | PublicationExistsException | UnknownAgentException | AgentExistsException
 +
            | InvalidIdentifierException e) {
 +
          // DAVID should not happen
 +
          e.printStackTrace();
 +
        }
 +
      }
 +
    } catch (IOException e1) {
 +
      throw new ImportFileException();
 +
    }
 +
  }
 +
        //...
 +
}
 +
</source>
  
<java5>
+
=== Registo de todos os tipos na rede social ===
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.
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
  
=== A classe de base ===
+
<source lang="java">
 +
class Network {
 +
        //...
 +
        void registerEntry(String[] fields) throws UnknownDataException,
 +
                        PublicationExistsException, UnknownAgentException, ClientExistsException,
 +
                        InvalidIdentifierException {
 +
                // Regular expression pattern to match an agent.
 +
                Pattern patAgent = Pattern.compile("^(PERSON|ORGANIZATION)");
 +
                // Regular expression pattern to match a message.
 +
                Pattern patMessage = Pattern.compile("^(MESSAGE)");
 +
                // Regular expression pattern to match a publication.
 +
                Pattern parPublication = Pattern.compile("^(IMAGE|NOTE|URI)");
 +
                // Regular expression pattern to match a publication.
 +
                Pattern parConnection = Pattern.compile("^(CONNECTION)");
  
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>).
+
                if (patAgent.matcher(fields[0]).matches()) {
 +
                        registerAgent(fields);
 +
                } else if (patMessage.matcher(fields[0]).matches()) {
 +
                        registerMessage(fields);
 +
                } else if (parPublication.matcher(fields[0]).matches()) {
 +
                        registerPublication(fields);
 +
                } else if (parConnection.matcher(fields[0]).matches()) {
 +
                        registerConnection(fields);
 +
                } else {
 +
                        throw new UnknownDataException(fields[0]);
 +
                }
 +
        }
 +
        //...
 +
}
 +
</source>
  
<java5>
+
Alternativa (melhor):
import java.util.ArrayList;
 
import java.lang.reflect.Constructor;
 
import java.lang.reflect.InvocationTargetException;
 
  
public abstract class Obra {
+
<source lang="java">
  static { System.out.println("Carregamento da classe Obra"); }
+
class Network {
 +
        //...
 +
  public void registerEntry(String... fields) throws UnknownDataException, PublicationExistsException,
 +
      UnknownAgentException, AgentExistsException, InvalidIdentifierException {
 +
    switch (fields[0]) {
 +
    case "PERSON", "ORGANIZATION" -> registerAgent(fields);
 +
    case "MESSAGE" -> registerEmail(fields);
 +
    case "IMAGE", "NOTE", "URI" -> registerPublication(fields);
 +
    case "CONNECTION" -> registerConnection(fields);
 +
    default -> throw new UnknownDataException(fields[0]);
 +
    }
 +
  }
 +
        //...
 +
}
 +
</source>
  
  public abstract void processa();
+
=== Registo de publicações na rede social ===
  
  static Obra cria(String dsc) {
+
Os campos relevantes ('''fields''') são: 0 - type; 1 - id/null; 2 - agent; other - pub-specific
    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;
 
    }
 
  }
 
  
}
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
</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.
+
<source lang="java">
 +
class Network {
 +
        //...
 +
        void registerPublication(String... fields) throws PublicationExistsException,
 +
                        UnknownAgentException, UnknownDataException {
 +
                int pubId = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();
 +
                int agtId = Integer.parseInt(fields[2]);
 +
                if (fields[0].equals("IMAGE")) {
 +
                        var pub = new Image(pubId, fields[3], fields[4]);
 +
                        var owner = fetchAgent(agtId);
 +
                        owner.addPublication(pubId, pub);
 +
                        setLastUUID(pubId);
 +
                        changed();
 +
                } else if (fields[0].equals("NOTE")) {
 +
                        var pub = new TextNote(pubId, fields[3], fields[4]);
 +
                        var owner = fetchAgent(agtId);
 +
                        owner.addPublication(pubId, pub);
 +
                        setLastUUID(pubId);
 +
                        changed();
 +
                } else if (fields[0].equals("URI")) {
 +
                        var pub = new URI(pubId, fields[3], fields[4]);
 +
                        var owner = fetchAgent(agtId);
 +
                        owner.addPublication(pubId, pub);
 +
                        setLastUUID(pubId);
 +
                        changed();
 +
                } else {
 +
                        throw new UnknownDataException(fields[0]);
 +
                }
 +
        }
 +
        //...
 +
}
 +
</source>
  
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'').
+
Alternativa (melhor):
 +
<source lang="java">
 +
class Network {
 +
        //...
 +
  public void registerPublication(String... fields)
 +
      throws PublicationExistsException, UnknownAgentException, UnknownDataException {
 +
    int pubId = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();
  
=== As classes das obras ===
+
    var pub = switch (fields[0]) {
 +
    case "IMAGE" -> new Image(pubId, fields[3], fields[4]);
 +
    case "NOTE" -> new TextNote(pubId, fields[3], fields[4]);
 +
    case "URI" -> new URI(pubId, fields[3], fields[4]);
 +
    default -> throw new UnknownDataException(fields[0]);
 +
    };
  
Para DVDs:
+
    var owner = fetchAgent(Integer.parseInt(fields[2]));
<java5>
+
    owner.addPublication(pubId, pub);
public class DVD extends Obra implements Rodopiável, Legível {
+
    setLastUUID(pubId);
  static { System.out.println("Carregamento da classe DVD"); }
+
    changed();
 +
  }
 +
        //...
 +
}
 +
</source>
  
  String _título;
+
=== Registo de agentes na rede social ===
  String _realizador;
 
  
  public DVD(String título, String realizador) {
+
Os campos relevantes ('''fields''') são: type|id-or-null|name|email|phone
    _título    = título;
 
    _realizador = realizador;
 
  }
 
  
  public String toString() { return "DVD:" + _título + ":" + _realizador; }
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
  public void ler()      { System.out.println("LER "      + this); }
 
  public void rodopiar() { System.out.println("RODOPIAR " + this); }
 
  
  public void processa() { rodopiar(); ler(); }
+
<source lang="java">
}
+
class Network {
</java5>
+
        //...
 +
        void registerAgent(String... fields) throws ClientExistsException, UnknownDataException {
 +
                int id = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();
 +
                if (fields[0].equals("PERSON")) {
 +
                        var agent = new Person(id, fields[2], fields[3], fields[4]);
 +
                        addAgent(id, agent);
 +
                        setLastUUID(id);
 +
                        changed();
 +
                } else if (fields[0].equals("ORGANIZATION")) {
 +
                        var agent = new Organization(id, fields[2], fields[3], fields[4]);
 +
                        addAgent(id, agent);
 +
                        setLastUUID(id);
 +
                        changed();
 +
                } else {
 +
                        throw new UnknownDataException(fields[0]);
 +
                }
 +
        }
 +
        //...
 +
}
 +
</source>
  
Para livros:
+
Alternativa (melhor):
<java5>
 
public class Livro extends Obra implements Folheável, Legível {
 
  static { System.out.println("Carregamento da classe Livro"); }
 
  
  String _título;
+
<source lang="java">
  String _autor;
+
class Network {
  String _isbn;
+
        //...
 +
  public void registerAgent(String... fields) throws AgentExistsException, UnknownDataException {
 +
    int id = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();
  
  public Livro(String título, String autor, String isbn) {
+
    var agent = switch (fields[0]) {
    _título = título;
+
    case "PERSON" -> new Person(id, fields[2], fields[3], fields[4]);
    _autor  = autor;
+
    case "ORGANIZATION" -> new Organization(id, fields[2], fields[3], fields[4]);
    _isbn  = isbn;
+
    default -> throw new UnknownDataException(fields[0]);
  }
+
    };
  
  public String toString() { return "Livro:" + _título + ":" + _autor + ":" + _isbn; }
+
    addAgent(id, agent);
  public void ler()    { System.out.println("LER "    + this); }
+
    setLastUUID(id);
  public void folhear() { System.out.println("FOLHEAR " + this); }
+
     changed();
 +
  }
 +
        //...
 +
}
 +
</source>
  
  public void processa() { folhear(); ler(); }
+
=== Registo de mensagens na rede social ===
}
 
</java5>
 
  
=== Aplicação exemplo ===
+
Os campos relevantes ('''fields''') são:
  
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).
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
<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.
+
<source lang="java">
 +
class Network {
 +
        //...
 +
        void registerMessage(String... fields) throws UnknownAgentException,
 +
                        UnknownDataException, InvalidIdentifierException {
 +
                if (fields[0].equals("MESSAGE")) {
 +
                        int msgId = Integer.parseInt(fields[1]);
 +
                        var sender = fetchAgent(Integer.parseInt(fields[2]));
 +
                        List<Recipient> recipients = new ArrayList<>();
 +
                        for (var recipientId : fields[3].split(",")) {
 +
                                Recipient recipient = fetchAgent(Integer.parseInt(recipientId));
 +
                                recipients.add(recipient);
 +
                        }
 +
                        assertValidIdentifier(msgId);
 +
                        var message = new EmailMessage(msgId, fields[2], sender, fields[3],
 +
                                        recipients, fields[4], fields[5], "", null);
 +
                        sendMessage(message);
 +
                        setLastUUID(msgId);
 +
                        changed();
 +
                } else {
 +
                        throw new UnknownDataException(fields[0]);
 +
                }
 +
        }
 +
        //...
 +
}
 +
</source>
  
  '''% java -Dobras=obras.txt Teste'''
+
=== Registo de ligações na rede social ===
  <font color="green">Carregamento da classe Obra</font>
+
 
  <font color="green">Carregamento da classe DVD</font>
+
Os campos relevantes ('''fields''') são:
  DVD:Era uma vez na amadora:Fernando Fonseca
+
 
  DVD:Lumiar selvagem:Pedro Fonseca
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
  <font color="green">Carregamento da classe Livro</font>
+
 
  Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890
+
<source lang="java">
  Livro:Bairro alto e o budismo zen:Zun Tse Fonseca:1234567891
+
class Network {
  DVD:48 horas para o exame:Orlando Fonseca
+
        //...
  Livro:Lux e o insucesso escolar - Uma visao matematica:Carlos Alberto Fonseca:1234567892
+
        void registerConnection(String[] fields) throws NumberFormatException,
  ****************
+
                        UnknownAgentException, UnknownDataException {
  RODOPIAR DVD:Era uma vez na amadora:Fernando Fonseca
+
                if (fields[0].equals("CONNECTION")) {
  LER DVD:Era uma vez na amadora:Fernando Fonseca
+
                        var a1 = fetchAgent(Integer.parseInt(fields[1]));
  RODOPIAR DVD:Lumiar selvagem:Pedro Fonseca
+
                        var a2 = fetchAgent(Integer.parseInt(fields[2]));
  LER DVD:Lumiar selvagem:Pedro Fonseca
+
                        boolean a1Accepting = (Integer.parseInt(fields[3]) != 0);
  FOLHEAR Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890
+
                        boolean a2Accepting = (Integer.parseInt(fields[4]) != 0);
  LER Livro:A arte de sobreviver no 36:Joao Fonseca:1234567890
+
                        a1.addConnection(new Connection(a1, a2, a1Accepting, a2Accepting));
  FOLHEAR Livro:Bairro alto e o budismo zen:Zun Tse Fonseca:1234567891
+
                        a2.addConnection(new Connection(a2, a1, a2Accepting, a1Accepting));
  LER Livro:Bairro alto e o budismo zen:Zun Tse Fonseca:1234567891
+
                        changed();
  RODOPIAR DVD:48 horas para o exame:Orlando Fonseca
+
                } else {
  LER DVD:48 horas para o exame:Orlando Fonseca
+
                        throw new UnknownDataException(fields[0]);
  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
+
        }
 +
        //...
 +
}
 +
</source>
 +
 
 +
== Utilização de Informação de Tipos em Tempo de Execução ==
 +
 
 +
Neste caso, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de descrições textuais desses objectos.
 +
 
 +
Este exemplo utiliza informação de tipos em tempo de execução. Note-se que este tipo de abordagem apenas é possível em linguagens que providenciem este tipo de informação em tempo de execução, como é o caso do Java.
 +
 
 +
* [[Informação de Tipos em Tempo de Execução (Java)]]
  
 
[[category:Ensino]]
 
[[category:Ensino]]
 
[[category:PO]]
 
[[category:PO]]
 
[[category:PO Exemplos|S]]
 
[[category:PO Exemplos|S]]

Latest revision as of 11:41, 28 September 2023

[Expand] Programação com Objectos
Introduction
Creation and Destruction
Inheritance & Composition
Abstraction & Polymorphism
Code Organization
Java Topics
Inner Classes
Enumerations
Data Structures
Exceptions
Input/Output
RTTI
Other Topics
JUnit Tests
UML Topics
Design Patterns
"Simple" Factory
Composite & Visitor
Command
Strategy & State
Template Method
Observer
Abstract Factory
Decorator & Adapter
Façade (aka Facade)

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

O processo de criação de objectos a partir de texto é um processo semelhante à compilação e implica interpretar esse texto e construir a semântica.

Utilização de Método Independente da Linguagem

O exemplo abaixo apresenta o caso simplificado de uma rede social. A rede social permite manipular as várias entidades, por forma a exercitar as várias interacções entre elas, nomeadamente a interacção de agentes e a publicação de conteúdos ("publicações", na nomenclatura usada no código abaixo).

Ficheiro de inicialização textual

PERSON|1|Luke Skywalker|wimp@resistance.org|123-456-789
PERSON|2|Darth Vader|vader@imperial.net|911
PERSON|3|Master Yoda|yoda@jedi.edu|1
PERSON|4|Montgomery Scott|scotty@starfleet.net|987-654-321
PERSON|5|James T. Kirk|kirk@starfleet.net|987-654-321
PERSON|6|Jean-Luc Picard|picard@starfleet.net|987-123-654
ORGANIZATION|7|Google|noreply@google.com|0
ORGANIZATION|8|Melkor & Sauron’s|recruiting@utumno.com|999
ORGANIZATION|9|Starfleet|info@starfleet.net|999
MESSAGE|10|1|3|cities|look at this http://bit.ly/p0XoDW
MESSAGE|11|2|1|we have to talk|Luke, I’m your father!
MESSAGE|12|1|2|Re: we have to talk|Nooooooooooo!!!!
MESSAGE|13|5|4|Teleporter|Beam me up, Scotty!
MESSAGE|14|6|4|Teleporter|Beam me up, Scotty!
NOTE|15|3|Cookies|Mix everything and apply a little Force for 30 seconds
NOTE|16|4|Dilithium crystals|Remember never to put them in the freezer
URI|17|1|Angry Ewoks|http://jedi.edu/ewoks/
URI|18|2|Imperial Bank|http://bank.imperial.net/
IMAGE|19|1|Tatooine|[desert scene with two suns setting]
CONNECTION|1|2|0|1
CONNECTION|1|3|1|1
CONNECTION|4|5|1|1
CONNECTION|4|6|1|1

Os formatos são os seguintes.

PERSON|id|name|email|phone
ORGANIZATION|id|name|email|phone
MESSAGE|id|from|to|subject|body
NOTE|id|owner|title|text
IMAGE|id|owner|description|content
URI|id|owner|description|uri
CONNECTION|id1|id2|id1-accepting?|id2-accepting?

Leitura do ficheiro de texto -- função principal de leitura

Função principal de leitura do ficheiro com a informação textual (no projecto, este ficheiro é tipicamente passado ao programa através da propriedade "import").

class Network {
        //...
  public void importFile(String name) throws ImportFileException {
    try (BufferedReader reader = new BufferedReader(new FileReader(name))) {
      String line;
      while ((line = reader.readLine()) != null) {
        String[] fields = line.split("\\|");
        try {
          registerEntry(fields);
        } catch (UnknownDataException | PublicationExistsException | UnknownAgentException | AgentExistsException
            | InvalidIdentifierException e) {
          // DAVID should not happen
          e.printStackTrace();
        }
      }
    } catch (IOException e1) {
      throw new ImportFileException();
    }
  }
        //...
}

Registo de todos os tipos na rede social

Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.

class Network {
        //...
        void registerEntry(String[] fields) throws UnknownDataException,
                        PublicationExistsException, UnknownAgentException, ClientExistsException,
                        InvalidIdentifierException {
                // Regular expression pattern to match an agent.
                Pattern patAgent = Pattern.compile("^(PERSON|ORGANIZATION)");
                // Regular expression pattern to match a message.
                Pattern patMessage = Pattern.compile("^(MESSAGE)");
                // Regular expression pattern to match a publication.
                Pattern parPublication = Pattern.compile("^(IMAGE|NOTE|URI)");
                // Regular expression pattern to match a publication.
                Pattern parConnection = Pattern.compile("^(CONNECTION)");

                if (patAgent.matcher(fields[0]).matches()) {
                        registerAgent(fields);
                } else if (patMessage.matcher(fields[0]).matches()) {
                        registerMessage(fields);
                } else if (parPublication.matcher(fields[0]).matches()) {
                        registerPublication(fields);
                } else if (parConnection.matcher(fields[0]).matches()) {
                        registerConnection(fields);
                } else {
                        throw new UnknownDataException(fields[0]);
                }
        }
        //...
}

Alternativa (melhor):

class Network {
        //...
  public void registerEntry(String... fields) throws UnknownDataException, PublicationExistsException,
      UnknownAgentException, AgentExistsException, InvalidIdentifierException {
    switch (fields[0]) {
    case "PERSON", "ORGANIZATION" -> registerAgent(fields);
    case "MESSAGE" -> registerEmail(fields);
    case "IMAGE", "NOTE", "URI" -> registerPublication(fields);
    case "CONNECTION" -> registerConnection(fields);
    default -> throw new UnknownDataException(fields[0]);
    }
  }
        //...
}

Registo de publicações na rede social

Os campos relevantes (fields) são: 0 - type; 1 - id/null; 2 - agent; other - pub-specific

Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.

class Network {
        //...
        void registerPublication(String... fields) throws PublicationExistsException,
                        UnknownAgentException, UnknownDataException {
                int pubId = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();
                int agtId = Integer.parseInt(fields[2]);
                if (fields[0].equals("IMAGE")) {
                        var pub = new Image(pubId, fields[3], fields[4]);
                        var owner = fetchAgent(agtId);
                        owner.addPublication(pubId, pub);
                        setLastUUID(pubId);
                        changed();
                } else if (fields[0].equals("NOTE")) {
                        var pub = new TextNote(pubId, fields[3], fields[4]);
                        var owner = fetchAgent(agtId);
                        owner.addPublication(pubId, pub);
                        setLastUUID(pubId);
                        changed();
                } else if (fields[0].equals("URI")) {
                        var pub = new URI(pubId, fields[3], fields[4]);
                        var owner = fetchAgent(agtId);
                        owner.addPublication(pubId, pub);
                        setLastUUID(pubId);
                        changed();
                } else {
                        throw new UnknownDataException(fields[0]);
                }
        }
        //...
}

Alternativa (melhor):

class Network {
        //...
  public void registerPublication(String... fields)
      throws PublicationExistsException, UnknownAgentException, UnknownDataException {
    int pubId = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();

    var pub = switch (fields[0]) {
    case "IMAGE" -> new Image(pubId, fields[3], fields[4]);
    case "NOTE" -> new TextNote(pubId, fields[3], fields[4]);
    case "URI" -> new URI(pubId, fields[3], fields[4]);
    default -> throw new UnknownDataException(fields[0]);
    };

    var owner = fetchAgent(Integer.parseInt(fields[2]));
    owner.addPublication(pubId, pub);
    setLastUUID(pubId);
    changed();
  }
        //...
}

Registo de agentes na rede social

Os campos relevantes (fields) são: type|id-or-null|name|email|phone

Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.

class Network {
        //...
        void registerAgent(String... fields) throws ClientExistsException, UnknownDataException {
                int id = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();
                if (fields[0].equals("PERSON")) {
                        var agent = new Person(id, fields[2], fields[3], fields[4]);
                        addAgent(id, agent);
                        setLastUUID(id);
                        changed();
                } else if (fields[0].equals("ORGANIZATION")) {
                        var agent = new Organization(id, fields[2], fields[3], fields[4]);
                        addAgent(id, agent);
                        setLastUUID(id);
                        changed();
                } else {
                        throw new UnknownDataException(fields[0]);
                }
        }
        //...
}

Alternativa (melhor):

class Network {
        //...
  public void registerAgent(String... fields) throws AgentExistsException, UnknownDataException {
    int id = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();

    var agent = switch (fields[0]) {
    case "PERSON" -> new Person(id, fields[2], fields[3], fields[4]);
    case "ORGANIZATION" -> new Organization(id, fields[2], fields[3], fields[4]);
    default -> throw new UnknownDataException(fields[0]);
    };

    addAgent(id, agent);
    setLastUUID(id);
    changed();
  }
        //...
}

Registo de mensagens na rede social

Os campos relevantes (fields) são:

Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.

class Network {
        //...
        void registerMessage(String... fields) throws UnknownAgentException,
                        UnknownDataException, InvalidIdentifierException {
                if (fields[0].equals("MESSAGE")) {
                        int msgId = Integer.parseInt(fields[1]);
                        var sender = fetchAgent(Integer.parseInt(fields[2]));
                        List<Recipient> recipients = new ArrayList<>();
                        for (var recipientId : fields[3].split(",")) {
                                Recipient recipient = fetchAgent(Integer.parseInt(recipientId));
                                recipients.add(recipient);
                        }
                        assertValidIdentifier(msgId);
                        var message = new EmailMessage(msgId, fields[2], sender, fields[3],
                                        recipients, fields[4], fields[5], "", null);
                        sendMessage(message);
                        setLastUUID(msgId);
                        changed();
                } else {
                        throw new UnknownDataException(fields[0]);
                }
        }
        //...
}

Registo de ligações na rede social

Os campos relevantes (fields) são:

Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.

class Network {
        //...
        void registerConnection(String[] fields) throws NumberFormatException,
                        UnknownAgentException, UnknownDataException {
                if (fields[0].equals("CONNECTION")) {
                        var a1 = fetchAgent(Integer.parseInt(fields[1]));
                        var a2 = fetchAgent(Integer.parseInt(fields[2]));
                        boolean a1Accepting = (Integer.parseInt(fields[3]) != 0);
                        boolean a2Accepting = (Integer.parseInt(fields[4]) != 0);
                        a1.addConnection(new Connection(a1, a2, a1Accepting, a2Accepting));
                        a2.addConnection(new Connection(a2, a1, a2Accepting, a1Accepting));
                        changed();
                } else {
                        throw new UnknownDataException(fields[0]);
                }
        }
        //...
}

Utilização de Informação de Tipos em Tempo de Execução

Neste caso, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de descrições textuais desses objectos.

Este exemplo utiliza informação de tipos em tempo de execução. Note-se que este tipo de abordagem apenas é possível em linguagens que providenciem este tipo de informação em tempo de execução, como é o caso do Java.