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

From Wiki**3

(A classe de base)
(Registo de ligações na rede social)
 
(42 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 ===
  
  <span style="font-weight: bold;">interface</span> Folheável {
+
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").
     <span style="color: #800000;">void</span> <span style="color: #000080;">folhear</span>();
+
 
 +
<source lang="java">
 +
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();
 +
    }
 
   }
 
   }
 +
        //...
 +
}
 +
</source>
 +
 +
=== 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.
 +
 +
<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)");
 +
 +
                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>
  
  <span style="font-weight: bold;">interface</span> Legível {
+
Alternativa (melhor):
     <span style="color: #800000;">void</span> <span style="color: #000080;">ler</span>();
+
 
 +
<source lang="java">
 +
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>
 +
 +
=== Registo de publicações na rede social ===
  
  <span style="font-weight: bold;">interface</span> Rodopiável {
+
Os campos relevantes ('''fields''') são: 0 - type; 1 - id/null; 2 - agent; other - pub-specific
    <span style="color: #800000;">void</span> <span style="color: #000080;">rodopiar</span>();
+
 
 +
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
 +
 
 +
<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>
 +
 
 +
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();
 +
 
 +
    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();
 
   }
 
   }
 +
        //...
 +
}
 +
</source>
  
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.
+
=== Registo de agentes na rede social ===
  
=== A classe de base ===
+
Os campos relevantes ('''fields''') são: type|id-or-null|name|email|phone
  
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>).
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
  
<java5>
+
<source lang="java">
import java.util.ArrayList;
+
class Network {
import java.lang.reflect.Constructor;
+
        //...
import java.lang.reflect.InvocationTargetException;
+
        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>
  
public abstract class Obra {
+
Alternativa (melhor):
  static { System.out.println("Carregamento da classe Obra"); }
 
  
  public abstract void processa();
+
<source lang="java">
 +
class Network {
 +
        //...
 +
  public void registerAgent(String... fields) throws AgentExistsException, UnknownDataException {
 +
    int id = (fields[1] != null) ? Integer.parseInt(fields[1]) : getUUID();
  
  static Obra cria(String dsc) {
+
    var agent = switch (fields[0]) {
    String dados[] = dsc.split(":");
+
    case "PERSON" -> new Person(id, fields[2], fields[3], fields[4]);
    try {
+
    case "ORGANIZATION" -> new Organization(id, fields[2], fields[3], fields[4]);
      ArrayList<String> ctorargs = new ArrayList<String>(dados.length-1);
+
    default -> throw new UnknownDataException(fields[0]);
      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;
 
    }
 
  }
 
  
}
+
    addAgent(id, agent);
</java5>
+
    setLastUUID(id);
 +
    changed();
 +
  }
 +
        //...
 +
}
 +
</source>
  
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.
+
=== Registo de mensagens na rede social ===
  
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'').
+
Os campos relevantes ('''fields''') são:
  
=== As classes das obras ===
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
  
Para DVDs:
+
<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>
  
  <b>public class</b> DVD <b>extends</b> Obra <b>implements</b> Rodopiável, Legível {
+
=== Registo de ligações na rede social ===
    <span style="color: #800000;">static</span> { <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"Carregamento da classe DVD"</span>); }
 
 
    <span style="font-weight: bold;color: #0095ff;">String</span> _título;
 
    <span style="font-weight: bold;color: #0095ff;">String</span> _realizador;
 
 
    <b>public</b> <span style="color: #000080;">DVD</span>(<span style="font-weight: bold;color: #0095ff;">String</span> título, <span style="font-weight: bold;color: #0095ff;">String</span> realizador) {
 
      _título    = título;
 
      _realizador = realizador;
 
    }
 
 
    <b>public</b> <span style="font-weight: bold;color: #0095ff;">String</span> <span style="color: #000080;">toString</span>() { <b>return</b> <span style="color: #dd0000;">"DVD:"</span> + _título + <span style="color: #dd0000;">":"</span> + _realizador; }
 
    <b>public</b> <span style="color: #800000;">void</span> <span style="color: #000080;">ler</span>()      { <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"LER "</span>      + <b>this</b>); }
 
    <b>public</b> <span style="color: #800000;">void</span> <span style="color: #000080;">rodopiar</span>() { <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"RODOPIAR "</span> + <b>this</b>); }
 
 
    <b>public</b> <span style="color: #800000;">void</span> <span style="color: #000080;">processa</span>() { <span style="color: #000080;">rodopiar</span>(); <span style="color: #000080;">ler</span>(); }
 
  }
 
  
Para livros:
+
Os campos relevantes ('''fields''') são:
  
  <b>public class</b> Livro <b>extends</b> Obra <b>implements</b> Folheável, Legível {
+
Excepções não relacionadas com a função actual são simplesmente enviadas para o chamador.
    <span style="color: #800000;">static</span> { <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"Carregamento da classe Livro"</span>); }
 
 
    <span style="font-weight: bold;color: #0095ff;">String</span> _título;
 
    <span style="font-weight: bold;color: #0095ff;">String</span> _autor;
 
    <span style="font-weight: bold;color: #0095ff;">String</span> _isbn;
 
 
    <b>public</b> <span style="color: #000080;">Livro</span>(<span style="font-weight: bold;color: #0095ff;">String</span> título, <span style="font-weight: bold;color: #0095ff;">String</span> autor, <span style="font-weight: bold;color: #0095ff;">String</span> isbn) {
 
      _título = título;
 
      _autor  = autor;
 
      _isbn  = isbn;
 
    }
 
 
    <b>public</b> <span style="font-weight: bold;color: #0095ff;">String</span> <span style="color: #000080;">toString</span>() { <b>return</b> <span style="color: #dd0000;">"Livro:"</span> + _título + <span style="color: #dd0000;">":"</span> + _autor + <span style="color: #dd0000;">":"</span> + _isbn; }
 
    <b>public</b> <span style="color: #800000;">void</span> <span style="color: #000080;">ler</span>()    { <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"LER "</span>    + <b>this</b>); }
 
    <b>public</b> <span style="color: #800000;">void</span> <span style="color: #000080;">folhear</span>() { <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"FOLHEAR "</span> + <b>this</b>); }
 
 
    <b>public</b> <span style="color: #800000;">void</span> <span style="color: #000080;">processa</span>() { <span style="color: #000080;">folhear</span>(); <span style="color: #000080;">ler</span>(); }
 
  }
 
  
=== Aplicação exemplo ===
+
<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>
  
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).
+
== Utilização de Informação de Tipos em Tempo de Execução ==
  
  <b>import</b><span style="color: #808000;"> java.util.ArrayList;</span>
+
Neste caso, apresenta-se a criação dinâmica de objectos de um determinado tipo, a partir de descrições textuais desses objectos.
  <b>import</b><span style="color: #808000;"> java.io.BufferedReader;</span>
 
  <b>import</b><span style="color: #808000;"> java.io.FileReader;</span>
 
  <b>import</b><span style="color: #808000;"> java.io.IOException;</span>
 
 
 
  <b>public class</b> Teste {
 
    <b>public</b> <span style="color: #800000;">static</span> <span style="color: #800000;">void</span> <span style="color: #000080;">main</span>(<span style="font-weight: bold;color: #0095ff;">String</span>[] args) <b>throws</b> <span style="font-weight: bold;color: #0095ff;">IOException</span> {
 
      <span style="font-weight: bold;color: #0095ff;">String</span> entrada = <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">getProperty</span>(<span style="color: #dd0000;">"obras"</span>, <span style="color: #dd0000;">"obras.txt"</span>);
 
      <span style="font-weight: bold;color: #0095ff;">BufferedReader</span> r = <b>new</b> <span style="font-weight: bold;color: #0095ff;">BufferedReader</span>(<b>new</b> <span style="font-weight: bold;color: #0095ff;">FileReader</span>(entrada));
 
      <span style="font-weight: bold;color: #0095ff;">String</span> linha;
 
      <span style="font-weight: bold;color: #0095ff;">ArrayList</span>&lt;Obra&gt; obras = <b>new</b> <span style="font-weight: bold;color: #0095ff;">ArrayList</span>&lt;Obra&gt;();
 
      <b>while</b> ((linha = r.<span style="color: #000080;">readLine</span>()) != <b>null</b>) {
 
        Obra o = Obra.<span style="color: #000080;">cria</span>(linha);
 
        obras.<span style="color: #000080;">add</span>(o);
 
        <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(o);
 
      }
 
      r.<span style="color: #000080;">close</span>();
 
      <span style="font-weight: bold;color: #0095ff;">System</span>.<span style="color: #000080;">out</span>.<span style="color: #000080;">println</span>(<span style="color: #dd0000;">"****************"</span>);
 
      <b>for</b> (Obra o: obras) o.<span style="color: #000080;">processa</span>();
 
    }
 
  }
 
  
A saída da aplicação de teste é a que se apresenta. Note-se a ordem de carregamento das classes.
+
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.
  
  '''% java -Dobras=obras.txt Teste'''
+
* [[Informação de Tipos em Tempo de Execução (Java)]]
  <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]]
+
[[category:Ensino]]
 +
[[category:PO]]
 +
[[category:PO Exemplos|S]]

Latest revision as of 11: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.