análise e projeto orientados a...
TRANSCRIPT
Análise e Projeto Orientados a Objetos
Padrões de atribuição de responsabilidade
Diretoria Acadêmica de Gestão e Tecnologia da Informação
Introdução
• Sistemas de software OO: conjunto de objetos que colaboram para atingir objetivos.
• A um objeto são atribuídas responsabilidades de acordo com seu papel no sistema.
• A atribuição de responsabilidades é uma tarefa crucial para a obtenção de um bom projeto OO.– Um bom projeto leva a um sistema robusto, de
fácil manutenção e de fácil evolução.
2
Projeto de objetos x UML
• Em projeto OO, a atribuição de responsabilidades é uma habilidade que pode ser desenvolvida através da prática e do conhecimento de soluções (padrões) e princípios estabelecidos.
• Conhecer UML não é garantia de um bom projeto OO.
– A UML atua como uma ferramenta de apoio.
• Conhecer uma linguagem OO também não é garantia de um bom projeto OO.
3
Projeto guiado por responsabilidade (PGR)
• Em PGR, objetos são idealizados como tendo responsabilidades.– Responsabilidades estão relacionadas com as obrigações de um
objeto em termos de seu papel no sistema.
• Basicamente, as responsabilidades são dos tipos fazer e conhecer.– Responsabilidades de fazer incluem: fazer algo propriamente
dito, como criar um objeto ou executar um cálculo; iniciar uma ação em outros objetos; controlar e coordenar atividades em outros objetos.
– Responsabilidades de saber incluem: ter conhecimento sobre dados encapsulados; conhecer objetos relacionados; ter conhecimento sobre coisas que ele pode derivar ou calcular.
4
Padrões GRASP
• General Responsibility Assignment Software Patterns.
• São uma formalização dos princípios orientadores para um bom projeto de objetos: encapsulamento, modularização, alta coesão e baixo acoplamento.
• Foram enunciados por Larman com o intuito de facilitar o aprendizado de projeto OO.
– Também são conhecidos como padrões de Larman.
5
O que são padrões?
• Segundo Larman, um padrão é um par problema/solução denominado e bem conhecido que pode ser aplicado em novos contextos, com conselhos sobre como aplicá-lo em situações novas e discussão sobre seus compromissos, implementações, variações, etc.
6
Padrão Criador
• Problema: quem cria um A?
• Solução: atribuir à classe B a responsabilidade de criar uma instância de A se uma ou mais das seguintes afirmativas for verdadeira (quanto mais melhor):– B contém A, ou agrega A de forma composta.
– B registra A.
– B usa A de maneira muito próxima.
– B contém os dados iniciais de A.
7
Padrão Criador - exemplo
• Jogo da memória, caso de uso Iniciar Partida.• Pós-condições: um jogo da memória foi criado com todas
as peças ocultas.• Fluxo básico:
1. Jogador informa a quantidade de peças da partida.2. Sistema verifica que a quantidade informada é válida, cria o
jogo com as peças embaralhadas e o caso de uso termina.
• Fluxo de exceção: passo 2– Sistema verifica que a quantidade informada é inválida, exibe
uma mensagem informativa para o usuário e o caso de uso retorna ao passo 1.
• Regras de negocio:– A quantidade informada pelo usuário deve ser um número par
maior ou igual a 4.
8
Padrão Criador - exemplo
• Modelo de domínio
9
Padrão Criador - exemplo
• Diagrama de classes de projeto
10
Padrão Criador - exemplo
• Problema: quem deve instanciar JogoDaMemoria?
– Quem agrega JogoDaMemoria? Ninguém.
– Quem registra JogoDaMemoria? Ninguém.
– Quem usa JogoDaMemoria de maneira próxima? IU (interface com o usuário).
– Quem contém os dados iniciais de JogoDaMemoria? IU.
11
Padrão Criador - exemplo
• Problema: quem deve criar as instâncias de Peca?– Quem agrega Peca? JogoDaMemoria.– Quem registra Peca? Ninguém.– Quem usa Peca de maneira próxima? JogoDaMemoria e
talvez IU.– Quem contém os dados iniciais de cada Peca? Devem ser
criadas n peças, com duas peças para cada símbolo (as peças ocorrem aos pares). Logo, na criação da peça deve-se atribuir o seu símbolo, que não muda ao longo da partida. Assumindo que os símbolos seguirão a sequência {1, 2, 3, ...}, pode-se perceber que o maior valor de símbolo será n/2. Quem possui n? JogoDaMemoria e IU. Como a criação das peças segue regras de negócio, IU não deve criar as peças para promover a coesão.
12
Padrão Criador - exemplo
13
Padrão Criador – exemplo (Java)
14
package dominio;
public enum EstadoPeca {
A_MOSTRA, OCULTA, ENCONTRADA
}
package dominio;
public class Peca {
private final int simbolo;
private EstadoPeca estado;
public Peca(int simbolo){
this.simbolo = simbolo;
estado = EstadoPeca.OCULTA;
}
}
Padrão Criador – exemplo (Java)
15
package dominio;
public class JogoDaMemoria {
private List<Peca> pecas;
public JogoDaMemoria(int qtdPecas) {
if (qtdPecas < 4 || qtdPecas % 2 != 0)
throw new IllegalArgumentException("Qtd de " +
"peças deve ser par e maior ou igual a 4.");
pecas = new ArrayList<Peca>();
for (int i = 1; i <= qtdPecas / 2; i++) {
pecas.add(new Peca(i));
pecas.add(new Peca(i));
}
Collections.shuffle(pecas);
}
Padrão Criador – exemplo (Java)
16
package iu;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Quantidade de peças: ");
int n = scan.nextInt();
JogoDaMemoria jogo = new JogoDaMemoria(n);
scan.close();
}
}
Padrão Criador – exemplo (C#)
namespace Dominio{
public enum EstadoPeca{
A_MOSTRA, OCULTA, ENCONTRADA
}
public class Peca{
private readonly int simbolo;
public int Simbolo { get { return simbolo; } }
private EstadoPeca estado;
public EstadoPeca Estado { get { return estado; } }
public Peca(int simbolo){
this.simbolo = simbolo;
this.estado = EstadoPeca.OCULTA;
}
}
17
Padrão Criador – exemplo (C#)
public class JogoDaMemoria{
private IList<Peca> pecas;
public JogoDaMemoria(int qtdPecas){
if (qtdPecas < 4 || qtdPecas % 2 != 0)
throw new ArgumentException
("Qtd de peças deve ser par e maior do que 3.");
pecas = new List<Peca>();
for (int i = 1; i <= qtdPecas / 2; i++){
pecas.Add( new Peca(i) );
pecas.Add( new Peca(i) );
}
MisturarPecas();
}
18
Padrão Criador – exemplo (C#)
private void MisturarPecas(){
Random rand = new Random();
for(int i = 0; i < pecas.Count * 100; i++){
int a = rand.Next(pecas.Count);
int b = rand.Next(pecas.Count);
Peca pecaA = pecas[a];
pecas[a] = pecas[b];
pecas[b] = pecaA;
}
}
}
}
19
Padrão Criador – exemplo (C#)namespace IU{
class Program{
static void Main(string[] args){
Console.WriteLine("Quantidade de peças: ");
while (true){
int n = Convert.ToInt16(Console.ReadLine());
try{
JogoDaMemoria jogo = new JogoDaMemoria(n);
break;
}catch (ArgumentException){
Console.WriteLine
("Quantidade inválida. Informe novamente: ");
}
}
}
}
} 20
Padrão Criador
• Vantagens:
– Promove o baixo acoplamento, o que implica em menor manutenção e mais oportunidades de reutilização.
• Contraindicações:
– Em situações em que a criação exige complexidade significativa, tais como reciclagem de objetos ou criação com base em uma dependência externa. Nestes casos, pode ser melhor delegar a criação para uma classe auxiliar como previsto nos padrões GoF.
21
Padrão Acoplamento Baixo
• Problema: como reduzir o impacto de modificação?
• Solução: atribuir responsabilidade de modo que o acoplamento (desnecessário) permaneça baixo. Use esse princípio para avaliar alternativas.
22
Padrão Coesão Alta
• Problema: como manter os objetos focados, inteligíveis e gerenciáveis e, como efeito colateral, apoiar o acoplamento baixo?
• Solução: atribuir responsabilidades de modo que a coesão permaneça alta. Use esse princípio para avaliar alternativas.
23
Padrão Controlador
• Problema: qual é o objeto, além da camada de IU, que recebe e coordena (controla) uma operação do sistema?
• Solução: atribuir a um objeto que representa uma dessas escolhas:– Representa todo o sistema, um objeto raiz, um
dispositivo dentro do qual o sistema executa ou um subsistema importante.
– Representa um cenário do caso de uso dentro do qual a operação ocorre.
24
Outros módulos
visão
Padrão Controlador
• Problema
25
:IU
:A
:B
:C
:D
:D
Operação
Objetos envolvidos
Quem deve coordenar a operação? :A, :B ou :C? Com
quem :IU deve comunicar-se?
Padrão Especialista na Informação
• Problema: qual é o princípio básico pelo qual atribuir responsabilidade aos objetos?
• Solução: atribua responsabilidade à classe que tenha informação necessária para satisfazê-la.
26
Aplicação de padrões – exemplo 1
• Problema: oferecer um mecanismo de acesso ao estado do jogo da memória para visualização na IU.
• Projeto da IU: cada peça será referenciada por um índice. Peças à mostra são representadas com seu símbolo, peças ocultas com um X e peças encontradas com um undescore.
27
Aplicação de padrões – exemplo 1
• Visão estática atual:
28
Aplicação de padrões – exemplo 1
• Visão dinâmica atual (para n=4):
29
:Peça
:Peça
:Peça
:Peça
:JogoDaMemoria:IU
Aplicação de padrões – exemplo 1
• Subproblema 1: quem, no domínio, deve oferecer o mecanismo de visualização? Ou seja, qual a classe da instância de domínio utilizada pela IU para obtenção do estado do jogo? Peça ou JogoDaMemoria?– Pelo Acoplamento Baixo: JogoDaMemoria, pois IU já
possui dependência com essa classe. Atualmente, IU não depende de peça. Estabelecer essa dependência aumentará o acoplamento.
– Por Especialista: a visualização do jogo depende dos estados individuais de todas as peças. Quem tem acesso a todas as peças? JogoDaMemoria.
– Por Controlador: JogoDaMemoria representa um objeto raiz.
30
Aplicação de padrões – exemplo 1
• Subproblema 2: escolher um mecanismo de visualização:
• Opção 1: fazer com que a IU tenha acesso às instâncias de Peca.
31
public class JogoDaMemoria{
private IList<Peca> pecas;
...
public Peca GetPeca(int i){
return pecas[i];
}
}
public class JogoDaMemoria {
private List<Peca> pecas;
...
public Peca getPeca(int i){
return pecas.get(i);
}
}
Aplicação de padrões – exemplo 1
• Opção 2: retornar representações do estado de uma peça.
32
public class JogoDaMemoria{
private IList<Peca> pecas;
...
public int GetPeca(int i){
if (pecas[i].IsAMostra())
return pecas[i].Simbolo;
else if (pecas[i].IsEncontrada())
return -1;
return 0; //peça oculta
}
}
public class JogoDaMemoria{
private List<Peca> pecas;
...
public int getPeca(int i){
Peca p = pecas.get(i);
if (p.isAMostra())
return p.getSimbolo();
else if (p.isEncontrada())
return -1;
return 0; // oculta
}
}
Aplicação de padrões – exemplo 1
• Qual opção escolher?– Por Acoplamento Baixo: opção 1 aumenta o acoplamento
pois força a IU a conhecer a interface de Peca. Consequentemente, diminui o encapsulamento entre módulos (camadas de IU e de domínio).Além disso, a opção aumenta a probabilidade de erros, pois como terá acesso às operações de Peca, a IU poderá alterar o estado das peças. Para evitar este problema, teríamos que utilizar verificações a respeito do solicitante da operação, ou utilizar um nível de visibilidade que permitisse a execução do método somente pelos objetos de domínio. Estas remediações levariam a um aumento da complexidade do código. Como este é um software de pequeno porte, este risco de erro é mínimo. No entanto, em sistemas maiores poderia ser um fator importante a ser considerado. Vamos adotar a opção 2.
33
Aplicação de padrões – exemplo 1
34
Aplicação de padrões – exemplo 1 (Java)
35
package dominio;
public class Peca {
...
public int getSimbolo(){
return simbolo;
}
public boolean isAMostra(){
return estado == EstadoPeca.A_MOSTRA;
}
public boolean isOculta(){
return estado == EstadoPeca.OCULTA;
}
public boolean isEncontrada(){
return estado == EstadoPeca.ENCONTRADA;
}
}
Aplicação de padrões – exemplo 1 (Java)
• Classe Main (parte 1)
36
package iu;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Quantidade de peças: ");
int n = scan.nextInt();
JogoDaMemoria jogo = new JogoDaMemoria(n);
print(jogo, n);
scan.close();
}
Aplicação de padrões – exemplo 1 (Java)
• Classe Main (parte 2)
37
public static void print(JogoDaMemoria jogo, int n){
for(int i = 1; i <= n; i++)
System.out.print(i + " ");
System.out.println();
for(int i = 1; i <= n; i++)
System.out.print("--");
System.out.println();
for(int i = 0; i < n; i++){
int j = jogo.getPeca(i);
String str;
if(j == -1) str = "_";
else if(j == 0) str = "X";
else str = String.valueOf(j);
System.out.print(str + " ");
}
System.out.println();
}
}
Aplicação de padrões – exemplo 1 (C#)
38
public class Peca{
...
public bool IsAMostra(){
return estado == EstadoPeca.A_MOSTRA;
}
public bool IsOculta(){
return estado == EstadoPeca.OCULTA;
}
public bool IsEncontrada(){
return estado == EstadoPeca.ENCONTRADA;
}
}
Aplicação de padrões – exemplo 1 (C#)
39
class Program{
static void Main(string[] args){
JogoDaMemoria jogo = null;
int n;
Console.WriteLine("Quantidade de peças: ");
while (true){
n = Convert.ToInt16(Console.ReadLine());
try{
jogo = new JogoDaMemoria(n);
break;
}catch (ArgumentException){
Console.WriteLine
("Quantidade inválida. Informe novamente: ");
}
print(jogo, n);
}
}
Aplicação de padrões – exemplo 1 (C#)
40
static void print(JogoDaMemoria jogo, int n){
for (int i = 1; i <= n; i++)
Console.Write(i + " ");
Console.WriteLine();
for (int i = 1; i <= n; i++)
Console.Write("--");
Console.WriteLine();
for (int i = 0; i < n; i++){
int j = jogo.GetPeca(i);
String str;
if (j == -1) str = "_";
else if (j == 0) str = "X";
else str = Convert.ToString(j);
Console.Write(str + " ");
} Console.WriteLine();
}
}
Aplicação de padrões – exemplo 2• Caso de uso: iniciar rodada• Pré-condições: Uma partida está em andamento. Não há peças à mostra
(1ª rodada) ou há duas peças à mostra (rodadas posteriores).• Pós-condições: uma peça tem seu estado alterado para à mostra. As peças
à mostra têm seu estado alterado para oculta.• Fluxo básico:
1. Jogador seleciona uma peça.2. Sistema verifica que a peça selecionada está oculta.3. Sistema verifica que há duas peças à mostra e altera seus estados para
oculta.4. Sistema altera o estado da peça selecionada para à mostra e o caso de uso
termina.
• Fluxo alternativo: passo 2– Sistema verifica que a peça selecionada não está oculta, informa o Jogador
para que selecione outra peça e caso de uso retorna ao passo 1.
• Fluxo alternativo: passo 3– Sistema verifica que não há peças à mostra (primeira rodada) e caso de uso
segue para o passo 4.
41
Aplicação de padrões – exemplo 2
• Entrada do usuário: índice da peça selecionada.
• Responsabilidades:– Execução da operação delegada pela IU:
JogoDaMemoria (por Controlador).
– Verificar estado da peça selecionada: JogoDaMemoria(por Especialista).
– Verificar se há peças à mostra e então ocultá-las: JogoDaMemoria (por Especialista).
– Alterar estado da peça selecionada: JogoDaMemoria(por Especialista).
42
Aplicação de padrões – exemplo 2
• Fluxo básico
43
Aplicação de padrões – exemplo 2 (Java)
44
public class JogoDaMemoria {
...
private void iniciarRodada(int i) {
//Pré-condições
List<Peca> exibidas = new ArrayList<Peca>();
for(Peca p : pecas){
if(p.isAMostra())
exibidas.add(p);
}
if(!(exibidas.size() == 0 || exibidas.size() == 2))
throw new IllegalStateException("Rodada já iniciada");
//Rodada
Peca selecionada = pecas.get(i);
if(!selecionada.isOculta())
throw new IllegalArgumentException("Selecione outra peça);
for (Peca p : exibidas)
p.setEstado(EstadoPeca.OCULTA);
selecionada.setEstado(EstadoPeca.A_MOSTRA);
}
}
Aplicação de padrões – exemplo 2 (Java)
45
public class JogoDaMemoria {
...
private void iniciarRodada(int i) {
//Pré-condições
List<Peca> exibidas = new ArrayList<Peca>();
for(Peca p : pecas){
if(p.isAMostra())
exibidas.add(p);
}
if(!(exibidas.size() == 0 || exibidas.size() == 2))
throw new IllegalStateException("Rodada já iniciada");
//Rodada
Peca selecionada = pecas.get(i);
if(!selecionada.isOculta())
throw new IllegalArgumentException("Selecione outra peça);
for (Peca p : exibidas)
p.setEstado(EstadoPeca.OCULTA);
selecionada.setEstado(EstadoPeca.A_MOSTRA);
}
}
Note o uso de diferentes exceções para permitir que o
código cliente diferencie as
situações de erro.
Aplicação de padrões – exemplo 2 (Java)
• Melhorando a coesão:
46
public List<Peca> getPecasAMostra() {
List<Peca> exibidas = new ArrayList<Peca>();
for (Peca p : pecas) {
if (p.isAMostra())
exibidas.add(p);
}
return exibidas;
}
private void iniciarRodada(int i) {
//Pré-condições
List<Peca> exibidas = getPecasAMostra();
if(!(exibidas.size() == 0 || exibidas.size() == 2))
throw new IllegalStateException("Rodada já iniciada");
//Rodada
...
}
Aplicação de padrões – exemplo 2 (Java)
47
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Quantidade de peças: ");
int n = scan.nextInt();
JogoDaMemoria jogo = new JogoDaMemoria(n);
print(jogo, n);
System.out.println("Escolha uma peça ");
int i = scan.nextInt();
jogo.iniciarRodada(i - 1);
print(jogo, n);
scan.close();
}
...
}
Aplicação de padrões – exemplo 2 (C#)
48
public class Peca{
...
public EstadoPeca Estado {
get{ return estado; }
set{ this.estado = value; }
}
...
}
public class JogoDaMemoria{
...
public void IniciarRodada(int i){
//Pré-condições
IList<Peca> exibidas = new List<Peca>();
Aplicação de padrões – exemplo 2 (C#)
49
foreach (Peca p in pecas){
if (p.IsAMostra())
exibidas.Add(p);
}
if (!(exibidas.Count == 0 || exibidas.Count == 2))
throw new InvalidOperationException("Rodada já iniciada");
//Rodada
Peca pecaSelecionada = pecas[i];
if (!pecaSelecionada.IsOculta())
throw new ArgumentException("Selecione outra peça");
foreach (Peca p in exibidas)
p.Estado = EstadoPeca.OCULTA;
pecaSelecionada.Estado = EstadoPeca.A_MOSTRA;
}
}
Aplicação de padrões – exemplo 2 (C#)
foreach (Peca p in pecas){
if (p.IsAMostra())
exibidas.Add(p);
}
if (!(exibidas.Count == 0 || exibidas.Count == 2))
throw new InvalidOperationException("Rodada já iniciada");
//Rodada
Peca pecaSelecionada = pecas[i];
if (!pecaSelecionada.IsOculta())
throw new ArgumentException("Selecione outra peça");
foreach (Peca p in exibidas)
p.Estado = EstadoPeca.OCULTA;
pecaSelecionada.Estado = EstadoPeca.A_MOSTRA;
}
} 50
Note o uso de diferentes exceções para permitir que o código cliente diferencie as
situações de erro.
Aplicação de padrões – exemplo 2 (C#)
• Melhorando a coesão
51
public class JogoDaMemoria
private IList<Peca> GetPecasAMostra(){
IList<Peca> exibidas = new List<Peca>();
foreach (Peca p in pecas){
if (p.IsAMostra())
exibidas.Add(p);
}
return exibidas;
}
public void IniciarRodada(int i){
//Pré-condições
IList<Peca> exibidas = GetPecasAMostra();
...
}
}
Aplicação de padrões – exemplo 2 (C#)
52
class Program{
static void Main(string[] args){
JogoDaMemoria jogo = null;
int n;
Console.WriteLine("Quantidade de peças: ");
while (true){
n = Convert.ToInt16(Console.ReadLine());
try{
jogo = new JogoDaMemoria(n);
break;
}catch (ArgumentException){
Console.WriteLine
("Quantidade inválida. Informe novamente: ");
}
}
Aplicação de padrões – exemplo 2 (C#)
53
print(jogo, n);
int i = Convert.ToInt16(Console.ReadLine());
jogo.IniciarRodada(i - 1);
print(jogo, n);
}
...
}
Aplicação de padrões – exemplo 3• Caso de uso: finalizar rodada• Pré-condições: Uma partida está em andamento. Há uma peça à mostra.• Pós-condições: uma peça tem seu estado alterado para à mostra. Uma
partida é finalizada.• Fluxo básico:
1. Jogador seleciona uma peça.2. Sistema verifica que a peça selecionada está oculta.3. Sistema altera o estado da peça selecionada para à mostra.4. Sistema verifica que as duas peças à mostra possuem o mesmo símbolo e altera seus
estados para encontrada.5. Sistema verifica que todas as peças foram encontradas, encerra a partida e caso de
uso termina.
• Fluxo alternativo: passo 2– Sistema verifica que a peça selecionada não está oculta, informa o Jogador para que
selecione outra peça e caso de uso retorna ao passo 1.
• Fluxo alternativo: passo 4– Sistema verifica que as duas peças à mostra não possuem o mesmo símbolo e o caso de
uso termina.
• Fluxo alternativo: passo 5– Sistema verifica que ainda faltam peças a serem encontradas e caso de uso termina. 54
Aplicação de padrões – exemplo 3
• Entrada do usuário: índice da peça selecionada.
• Responsabilidades:
– Execução da operação delegada pela IU: JogoDaMemoria (por Controlador).
– Verificar estado da peça selecionada: JogoDaMemoria(por Especialista).
– Verificar símbolos das peças à mostra: JogoDaMemoria (por Especialista).
– Finalizar a partida: JogoDaMemoria (por Especialista).
55
Aplicação de padrões – exemplo 3 (Java)
56
Aplicação de padrões – exemplo 3 (Java)
57
Aplicação de padrões – exemplo 3 (C#)
58
public class JogoDaMemoria{
...
public bool fimDeJogo = false;
public void FinalizarRodada(int i){
//Pré-condições
IList<Peca> exibidas = GetPecasAMostra();
if (exibidas.Count != 1)
throw new InvalidOperationException("Início de rodada");
Peca pecaSelecionada = pecas[i];
if(!pecaSelecionada.IsOculta())
throw new ArgumentException("Selecione outra peça");
//Rodada
pecaSelecionada.Estado = EstadoPeca.A_MOSTRA;
if (pecaSelecionada.Simbolo == exibidas[0].Simbolo){
pecaSelecionada.Estado = EstadoPeca.ENCONTRADA;
exibidas[0].Estado = EstadoPeca.ENCONTRADA;
}
Aplicação de padrões – exemplo 3 (C#)
59
if (GetQtdPecasEncontradas() == pecas.Count)
fimDeJogo = true;
}
public int GetQtdPecasEncontradas(){
int qtd = 0;
foreach(Peca p in pecas)
if(p.IsEncontrada())
qtd++;
return qtd;
}
public bool IsFimDeJogo(){
return fimDeJogo;
}
}
Aplicação de padrões – exemplo 3 (C#)
60
class Program{
static void Main(string[] args){
JogoDaMemoria jogo = null;
int n;
Console.WriteLine("Quantidade de peças: ");
while (true){
n = Convert.ToInt16(Console.ReadLine());
try{
jogo = new JogoDaMemoria(n);
break;
}catch (ArgumentException){
Console.WriteLine
("Quantidade inválida. Informe novamente: ");
}
}
print(jogo, n);
Aplicação de padrões – exemplo 3 (C#)
61
while(!jogo.IsFimDeJogo()){
int i = Convert.ToInt16(Console.ReadLine());
jogo.IniciarRodada(i - 1);
print(jogo, n);
i = Convert.ToInt16(Console.ReadLine());
jogo.FinalizarRodada(i - 1);
print(jogo, n);
}
}
...
}
Aplicação de padrões – exemplo 4
• Na forma atual, IU deve ficar alternando entre os métodos iniciarRodada e finalizarRodada. Assim, IU deve saber se é o início ou finalização da rodada. Este controle pode ser feito pela própria IU (menor coesão) ou por JogoDaMemoria, que disponibilizaria um método de acesso ao estado da rodada.
• Outra opção e é delegar o controle do estado da rodada a JogoDaMemoria, que disponibilizaria um único método a ser chamado pela IU para execução de uma rodada (favorece o encapsulamento).
62
Aplicação de padrões – exemplo 4
• Pior:
• Melhor:
63
Aplicação de padrões – exemplo 4 (Java)
64
Aplicação de padrões – exemplo 4 (C#)
65
public class JogoDaMemoria{
...
public void ExibirPeca(int i){
//Escolhe método com base nas pré-condições
IList<Peca> pecasEmExibicao = GetPecasAMostra();
if(pecasEmExibicao.Count == 0 || pecasEmExibicao.Count == 2)
IniciarRodada(i);
else if (pecasEmExibicao.Count == 1)
FinalizarRodada(i);
else
throw new InvalidOperationException
("Há mais do que duas peças em exibição");
}
}
Aplicação de padrões – exemplo 4 (Java)
66
Aplicação de padrões – exemplo 4 (Java)
• IU com tratamento de exceção
67
Aplicação de padrões – exemplo 4 (C#)
68
class Program{
static void Main(string[] args){
...
while(!jogo.IsFimDeJogo()){
int i = Convert.ToInt16(Console.ReadLine());
try{
jogo.ExibirPeca(i - 1);
}catch(ArgumentException){
Console.WriteLine("Peça inválida");
}
print(jogo, n);
}
}
...
}
Padrão Acoplamento Baixo
• Vantagens:– Minimiza mudanças causadas por alterações em
outros componentes.– Aumenta o potencial de reuso.
• Contraindicações:– Note que o acoplamento em si não é ruim, mas sim o
acoplamento a elementos instáveis. O acoplamento a elementos estáveis, tais como objetos da biblioteca padrão, pode não ser um problema. Nem sempre deve se buscar o projeto com maior flexibilidade possível. Foque nos pontos que apresentam instabilidade.
69
Padrão Coesão Alta
• Vantagens:– Mais clareza e facilidade de compreensão no projeto.– Manutenção e evolução simplificados.– Frequentemente, apoia o acoplamento baixo e o
potencial de reuso.
• Contraindicações:– Em situações em que o desempenho computacional é
importante e é degradado pela decomposição funcional em múltiplas classes. Nesses casos, pode ser melhor agregar as funcionalidades em um nível de granularidade maior, diminuindo assim o número de classes.
70
Padrão Controlador
• Vantagens:
– Aumento das possibilidades de reutilização.
– Promoção da coesão alta.
– Oportunidade de raciocinar sobre o estado do caso de uso.
71
Padrão Especialista na Informação
• Vantagens:– Geralmente promove encapsulamento, acoplamento
baixo e alta coesão.
• Contraindicações:– Em algumas situações, a solução sugerida por
Especialista leva a problemas de acoplamento e de coesão. Especialista é geralmente o primeiro padrão a considerar na escolha de um objeto responsável, mas este padrão não deve se sobrepor aos princípios fundamentais de encapsulamento, alta coesão e baixo acoplamento.
72
Padrão Polimorfismo
• Problema: como tratar alternativas com base no tipo? Como criar componentes de software interconectáveis?
• Solução: ao invés de lógica condicional, use operações polimórficas.
73
Padrão Polimorfismo: exemplo 2
• API JDBC
74
Padrão Polimorfismo
• Vantagens:
– Aumenta a flexibilidade do sistema: novas implementações podem ser introduzidas sem afetar os clientes.
• Contraindicações:
– Polimorfismo aumenta a complexidade do projeto. Por isso, não se deve buscar a flexibilidade máxima do sistema, tornando-o à prova de modificações. A flexibilidade deve ser buscada nos pontos em que a variabilidade é conhecida e realmente necessária.
75
Padrão Invenção Pura
• Problema: que objeto deve ter a responsabilidade quando não se quer violar a Coesão Alta e o Acoplamento Baixo ou outros objetivos, mas as soluções oferecidas pelas classes de domínio não são apropriadas?
• Solução: atribuir a responsabilidade a uma classe de conveniência que não represente um conceito do domínio, ou seja, uma classe criada especificamente para esse fim: uma invenção pura.
76
Padrão Invenção Pura: exemplo
• Sistemas de informação lidam com grandes quantidades de dados. É ineficiente manter todas as instâncias de domínio (entidades) em memória. Um banco de dados é tipicamente utilizado como meio persistente.
• Considerando o Sistema de Revenda de Veículos, quem deve ter as responsabilidades relativas à persistência das instâncias da classe Fabricante?
• Por Especialista: a própria classe Fabricante, pois é quem tem acesso às informações necessários (valores dos atributos).
77
Padrão Invenção Pura: exemplo
• Implicações:– A tarefa exige um número grande de operações
relacionadas ao banco de dados, nenhuma delas relativa ao conceito de fabricante. Assim, a coesão de Fabricante é reduzida.
– Fabricante fica acoplada às classes da biblioteca de acesso ao banco dados (e.g, JDBC, ADO, JPA, etc), de modo que seu acoplamento aumenta. O pior: este acoplamento não é com outras classes de domínio.
– Persistir objetos em um banco de dados é uma tarefa geral, sendo necessária para outras entidades tais como Veículo, Venda e Compra. Colocar essas responsabilidades em Fabricante sugere que haverá reuso inadequado ou muita duplicação de código.
78
Padrão Invenção Pura: exemplo
• Solução por Invenção Pura: criar uma classe para receber estas responsabilidades.
– Classe RepositorioFabricante.
79
Padrão Invenção Pura
• Vantagens:– A Coesão Alta é favorecida porque as
responsabilidades são decompostas em uma classe que focaliza apenas um conjunto específico de tarefas relacionadas.
– Aumento do potencial de reuso.
• Contraindicações:– Em caso de abuso, pode levar a muitos objetos de
comportamento com responsabilidades não compartilhadas, acarretando em uma abordagem mais procedural do que orientada a objetos.
80
Padrão Indireção
• Problema: a quem devemos atribuir a responsabilidade de maneira a evitar o acoplamento direto entre dois objetos?
• Solução: atribuir a responsabilidade de ser o mediador entre outros componentes ou serviços a um objeto intermediário, para que eles não sejam diretamente acoplados.
81
Padrão Indireção: exemplo 2
• Geração de números aleatórios no exemplo da aula sobre mocks (Craps).
82
Padrão Indireção
• Vantagens:
– Favorece o baixo acoplamento.
– Favorece a manutenibilidade do sistema, pois os clientes tornam-se independentes da implementação encapsulada pelo objeto intermediário.
83
Padrão Variações Protegidas
• Problema: como projetar objetos, subsistemas e sistemas de modo que as variações ou a instabilidade nesses elementos não tenham um impacto indesejável sobre outros elementos?
• Solução: identificar pontos de variação ou instabilidade e atribuir responsabilidades com base em uma interface (no sentido amplo) estável.
84
Padrão Variações Protegidas : exemplo 1
• Jogo da memória: protocolo textual que informa o símbolo de uma peça com base no seu estado.
– Peça à mostra: retorna símbolo.
– Peça encontrada: retorna -1.
– Peça oculta: retorna zero.
85
Padrão Variações Protegidas : exemplo 2
• API JDBC: encapsulamento de protocolo proprietário.
86
Aplicação Java
JDBC API
Oracle
MySQL
SQL Server
TNS
MySQL Client/Server
Protocol
MS-TDS
MS-SSTDS
MS-SMP
MS-BINXML
MS-SSCLRT
Padrão Variações Protegidas
• Vantagens:
– Aumenta a extensibilidade.
– Diminui o acoplamento.
• Contraindicações:
– Como em Polimorfismo, a aplicação do padrão deve ser feita nos pontos de variação reais, e não nos pontos de variação especulativa.
87
Leitura obrigatória
• Livro do Larman:
– 3ª edição: capítulos 17, 18 e 25.
– 2ª edição: capítulos 16, 17 e 22.
88
Referências
• LARMAN, Craig. Utilizando UML e padrões: uma introdução à análise e ao projeto orientados a objeto e ao desenvolvimento iterativo. Porto Alegre: Bookman, 2007, 3ª. ed.
89
Informações bibliográficas
• Autor: Alexandre Gomes de Lima
• Data de atualização: novembro de 2018
• Licença de uso: Creative Commons BY-SA (Atribuição-CompartilhaIgual)
90