"Simple" Factory (padrão de desenho): Difference between revisions
From Wiki**3
New page: {{TOCright}}O padrão "simple" factory fornece uma forma de criar objectos a partir de uma descrição externa. == Exemplo == Neste exemplo, apresenta-se a criação dinâmica de objecto... |
|||
| (43 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. | |||
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 === | ||
<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> | |||
= | 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> | |||
=== 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"). | |||
<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> | |||
Alternativa (melhor): | |||
<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 === | |||
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. | |||
<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> | |||
=== 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. | |||
<source lang="java"> | |||
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]); | |||
} | |||
} | |||
//... | |||
} | |||
</source> | |||
Alternativa (melhor): | |||
<source lang="java"> | |||
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(); | |||
} | } | ||
//... | |||
} | |||
</source> | |||
=== 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. | |||
<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> | |||
=== 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. | |||
<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: | [[category:Ensino]] | ||
[[category:PO]] | |||
[[category:PO Exemplos|S]] | |||
Latest revision as of 09:41, 28 September 2023
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.