"Simple" Factory (padrão de desenho)

From Wiki**3

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.