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

From Wiki**3

Root (talk | contribs)
Root (talk | contribs)
 
(39 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.
{{NAVPO}}
{{TOCright}}
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>
  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]]
Os campos relevantes ('''fields''') são:
 
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
 
<source lang="java">
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]);
                }
        }
        //...
}
</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:PO]]
[[category:PO Exemplos|S]]

Latest revision as of 09:41, 28 September 2023

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.