Buscar

Princípios de Projeto de Software - parte 1

Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original

Princípios de Projeto
Prof. Clayton Vieira Fraga Filho
site: www.claytonfraga.pro.br
e-mail: claytonfraga@gmail.com
COM10508 – Projeto de Sistemas de Software
1
Princípios de projetos Orientados a Objetos
Princípio do Aberto/Fechado (Open/Closed Principle - OCP)
Princípio da Substituição de Liskov (Liskov Substitution Principle -LSP)
Princípio da responsabilidade única (Single Responsability Principle - SRP)
Princípio da Inversão de Dependência – (Dependency Inversion Principle - DIP)
Princípio da Segregação de Interfaces (Interface Segregation Principle - ISP)
Não se repita (Don’t Repeat Yourself - DRY) 
Lei de Demeter
Princípio do Reuso por composição
2
Princípio do Aberto/Fechado 
Open/Closed Principle (OCP)
Um módulo deve ser aberto para extensão mas fechado para modificação.
Objetivo: criar módulos que sejam extensíveis sem precisarem ser modificados
Consequências 
Mudanças não se propagam para código que já existe;
Se você não precisa mudar um código, então provavelmente você não vai quebrá-lo;
3
Princípio do Aberto/Fechado 
Open/Closed Principle (OCP)
class Modem {
 void logon(TipoModem tipo) {
 if (tipo == TipoModem.Hayes) {
 discaHayes();
 } else if (tipo == TipoModem.Courrier) {
 discaCourrier();
 } else if (tipo == TipoModem.Ernie) {
 discaErnie();
 }
 }
 void discaHayes() {...}
	void discaCourrier() {...}
	void discaErnie() {...}
	
}
Modem m = new Modem();
m.logon(TipoModem.HAYES);
4
Princípio do Aberto/Fechado 
Open/Closed Principle (OCP)
O que acontece se precisarmos adicionar um novo tipo de modem?
class Modem {
 void logon(TipoModem tipo) {
 if (tipo == TipoModem.Hayes) {
 discaHayes();
 } else if (tipo == TipoModem.Courrier) {
 discaCourrier();
 } else if (tipo == TipoModem.Ernie) {
 discaErnie();
 } else if (tipo == TipoModem.X) {
 discaX();
 }
 }
 void discaHayes() {... }
	void discaCourrier() {... }
	void discaErnie() {... }
	void discaX() {... }
}
5
Princípio do Aberto/Fechado 
Open/Closed Principle (OCP)
Conformidade com o princípio
interface Modem {
 void disca();
}
class Hayes implements Modem {
 public void disca {... }
}
class Courrier implements Modem { 
 public void disca {... }
}
class Ernie implements Modem {
 public void disca {... }
}
public void logon(Modem modem) {
	modem.disca();
}
6
Princípio da Substituição de Liskov
Liskov Substitution Principle (LSP)
Sempre que uma classe cliente espera uma instância de uma classe base A, uma instância de uma subclasse B de A deve poder ser usada no lugar.(relação É-UM) 
Se a nova classe puder ser utilizada em qualquer contexto onde a classe original podia ser utilizada, então diz-se que a nova classe é um subtipo da superclasse. Se esta substituição é possível, a subclasse obedece ao Princípio de Substituição de Liskov (por Barbara Liskov). 
Uma das restrições em subtipos é que o significado dos métodos não deve ser alterado
"Subclasses devem poder substituir suas superclasses"
Objetivo: evitar que premissas em relação a classes base não sejam quebradas por suas subclasses
7
Princípio da Substituição de Liskov
Liskov Substitution Principle (LSP)
Uma subclasse deve manter todo o comportamento externo observável de sua superclasse
Uma subclasse pode estender o comportamento observável externo, mas nunca modificá-lo
Comportamento observável externo = Comportamento dos métodos públicos
8
Princípio da Substituição de Liskov
Liskov Substitution Principle (LSP)
List
+insert()
+delete()
+find()
Queue
+enqueue()
+dequeue()
+isEmpty()
List
+insert()
+delete()
+find()
Queue
+enqueue()
+dequeue()
+isEmpty()
List
+insert()
+delete()
+find()
Queue
+enqueue()
+dequeue()
+isEmpty()
MyList
Quando se usa Lista para implementar Fila (em Java), use composição, não herança.
A intenção é usar List em implementações apenas de listas.
9
Princípio da Substituição de Liskov
Liskov Substitution Principle (LSP)
Há casos em que a substituição pode não ser necessária: Generalização
Por isso criamos a classe Employee como um "espaço reservado" para essas propriedades comuns.
Assim, as quatro subclasses não precisam ser substituíveis
Employee
+printInfo()
Faculty
+printInfo()
Staff
+printInfo()
Secretary
+printInfo
Engineer
+printInfo()
generalization
10
Princípio da responsabilidade única 
SRP - Single Responsability Principle
Este é o pensamento-chave para o princípio citado pela primeira vez por Tom DeMarco em 1979, no livro Structured Analysis and Systems Specification. O SRP combate o desenvolvimento de Classes “faz tudo” e também é conhecido como o Princípio da Coesão.
O princípio diz que:
“Nunca deve haver 
mais de um motivo para 
uma classe ser alterada”
11
Princípio da responsabilidade única 
SRP - Single Responsability Principle
Se uma classe possuir mais de uma responsabilidade, deve-se considerar sua decomposição em duas ou mais classes;
Cada responsabilidade é um eixo de mudança e as fontes de mudança devem ser isoladas;
Baseado no princípio da coesão funcional, uma classe deve ter uma única responsabilidade; 
Violação do princípio
Adequação ao princípio
Exemplo:
12
Princípio da responsabilidade única 
SRP - Single Responsability Principle
class ProcessadorDePedidosDeVendas
{
 private UsuarioLogado usuarioLogado;
 private List<ItensDeVendaDTO> itensDeVendaDTO;
 private PedidoDeVenda pedidoDeVenda;
 private List<ItensDeVenda> itensDeVenda;
 private Empresa empresa;
 private DadosAdicionaisPedido dadosAdicionais;
 private int pedidoVendaId;
 /*Mais algumas propriedades*/
 public ProcessadorDePedidosDeVendas(UsuarioLogado usuarioLogado, List<ItensDeVendaDTO> itensDeVendaDTO, int pedidoVendaId, List<ItensDeVenda> itensDeVenda, Empresa empresa, DadosAdicionaisPedido dadosAdicionais,/*mais alguns parametros*/)
 {
 //atribui todas as variáveis
 }
 //este método tinha cerca de 300 linhas
 //chamando mais alguns métodos em outras 400 linhas
 public void Processar()
 {
 //verificar se o pedido id existe e carrega do banco
 //cria um novo pedido caso necessário
 //verifica as permissões do usuário logado
 //verifica algumas informações da empresa
 //percorre todos os itensDTO transoformando em itens
 //ententa que isso está em um foreach com mais de 100 linhas
 //buscando várias informações no banco, fazendo cálculos, etc
 //persiste os itens no banco
 //calcula alguns valores do pedido
 //persiste o pedido
 }
}
13
Não se repita
Don’t Repeat Yourself(DRY)
Cada informação e comportamento do seu sistema deve estar em um único lugar. 
O princípio diz que:
“Não deve existir 
código repetido 
através do sistema.”
14
Princípio da Inversão de Dependência
(Dependency Inversion Principle - DIP)
"Dependa de abstrações. Não dependa de coisas concretas"
ou Dependa sempre de interfaces abstratas, e nunca de classes concretas
Justificativa:
Implementações concretas são mais propensas a mudanças
Possibilidade de alterar (ou incluir novas) implementações sem necessidade de alterar classes clientes
15
Princípio da Inversão de Dependência
(Dependency Inversion Principle - DIP)
class Cobranca {
	void calcula() {
		// faz varias coisas
		db.insere("TB_LOG", "Resultado: " + calc);
	}
	...
}
class Pedido {
	void criaPedido() {
		// faz varias coisas
		db.insere("TB_LOG", "Novo pedido: " + id);
	}
}
	...
16
Princípio da Inversão de Dependência
(Dependency Inversion Principle - DIP)
interface Log {
	void escreve(mensagem);
}
class DbLog implements Log {
	public void escreve(String mensagem) {
		db.insere("TB_LOG", mensagem);
	}
}
17
Princípio da Inversão de Dependência
(Dependency Inversion Principle - DIP)
class Cobranca {
	Log log;
	Calcula(Log log) { this.log = log; }
	void calcula() {
		// faz varias coisas
		log.escreve("Resultado: " + calc);
	}
	...
}
class Pedido {
	void criaPedido() {
		// faz varias coisas
		log.escreve("Novo pedido: "
+ id);
	}
	...
}
Log log = new DbLog();
Cobranca c = new Cobranca(log);
18
Princípio da Segregação de Interfaces
Interface Segregation Principle (ISP)
O princípio da segregação de interfaces ajuda a resolver problemas de interface poluída. São classes cuja interface não possui coesão tornando as interfaces poluídas e disponibilizando métodos desnecessariamente.
A Interface Segregation Principle (ISP) afirma que as classes não devem implementar interfaces não usadas. 
"Muitas interfaces específicas para clientes é melhor do que uma única interface de propósito geral"
19
Princípio da Segregação de Interfaces
Interface Segregation Principle (ISP)
Interface Poluída:
Uma classe A que contém métodos que não utiliza e outra classe B que as utiliza, a classe A será afetada pelas mudanças que a classe B necessitar, caso ambas assinem o mesmo contrato.
Problema:
20
Princípio da Segregação de Interfaces
Interface Segregation Principle (ISP)
Interface Poluída:
Devemos separar as interfaces para esse tipo de acoplamento.
Sendo assim, algumas classes usam (assinam) um grupo de operações e outras classes usam outros grupos. Classes devem depender somente dos métodos que serão utilizados.
21
Princípio Lei de Demeter
Law of Demeter - LOD
A lei de Demeter é um conjunto de regras para construir sistemas visando baixo acoplamento, também conhecida como Princípio do menor Conhecimento e fale somente com os amigos.
Cada unidade deve somente utilizar um conjunto limitado de unidades de outras unidades.
Cada unidade deve falar somente com seus amigos e não com “estrangeiros”.
22
Princípio Lei de Demeter
Law of Demeter - LOD
Em orientação a objeto
Um método M de um objeto O somente poderia acessar métodos de outros objetos que sigam as diretrizes:
Seja parâmetro de M
Um objeto que M criou
Um método do próprio objeto O
Objeto diretamente relacionado com o objeto O
Uma variável global acessível pelo objeto O
23
Princípio Lei de Demeter
Law of Demeter - LOD
public void coletarPagamento(){
 for (Cliente cliente : clientes) {
 if (cliente.getMinhacarteira().getValor()>= nota) {
 cliente.getMinhacarteira().subtractValor(nota);
 fundoColeta += nota;
 }
 else {
 System.out.println("Sem dinheiro");
 }
 }
}
A finalidade do método coletarPagamento, é coletar pagamento dos clientes por produtos ou serviços oferecido pela classe Jornaleiro, perceba que dentro do método, a classe Jornaleiro acessa a classe Carteira que mantem associação apenas com Cliente, passando a conhecer tudo que tem na Carteira do Cliente, ou seja na classe Carteira, coisa que somente o Cliente deve saber. 
O diagrama de classes ao lado exibe o forte acoplamento criado, pois foi criado uma dependência com a classe Carteira, quebrando o principio de LOD.
24
Princípio Lei de Demeter
Law of Demeter - LOD
1º - Para resolver este problema, temos que remover o método getMinhacarteira() da classe Cliente e criar um método getfazerPagamento, agora o Cliente recebe a nota e faz o pagamento para o Jornaleiro, cenário que acontece no mundo real. 
public double getFazerPagamento(double nota) {
 double valorPago = 0
 if (minhacarteira.getValor() >= nota) {
 minhacarteira.subtractValor(nota);
 valorPago = nota;
 }
 return valorPago;
}
2º - É preciso modificar o método coletarPagamento() da classe Jornaleiro, pois ele não acessa mais a classe Carteira para retirar a pagamento que lhe é devido.
public void coletarPagamento(){
 for (Cliente cliente : clientes) {
 double pagamento = cliente.getFazerPagamento(nota);
 if (pagamento !=0) {
 fundoColeta += pagamento;
 }
 }
}
25
Princípio Lei de Demeter
Law of Demeter - LOD
A dependência da classe Jornaleiro com a classe Carteira foi removida
26
Princípio Lei de Demeter
Law of Demeter - LOD
Exemplo clássico do cachorro: Quando você precisa que um cachorro ande, você dá a ordem para as pernas diretamente, ou para o cachorro? 
package br.com.celsomartins.cachorro;
import java.util.ArrayList;
import java.util.List;
public class Cachorro {
private List <Pata> patas = new ArrayList <Pata>();
 public boolean andar() {
 for (Pata pata: patas) {
 if (!pata.andar()) {
 return false;
 }
 }
 return true;
 }
}
package br.com.celsomartins.cachorro;
public class Pata {
 
 public boolean andar() {
 return true;
 }
}
A diferença é clara: 
disso: 
for (Pata pata: cachorro.getPatas()){
 pata.andar();
}
para: 
	cachorro.andar();
27
Princípio do Reuso por composição
The Composite Reuse Principle (CRP)
Prefira a composição (delegação) em relação a herança
Herança excessiva pode causar fragilidade e hierarquias complexas de classes 
Com herança podemos sobrescrever um método, às vezes indesejável. Na composição os métodos devem ser utilizados como foram definidos 
Novas funcionalidades podem ser agregadas sem alteração no código existente (Princípio do Aberto- Fechado)
Delegação pode ser vista como um mecanismo de reutilização no nível do objeto, enquanto a herança é um mecanismo de reuso no nível de classe.
28
Princípio do Reuso por composição
The Composite Reuse Principle (CRP)
Por exemplo, suponha que uma classe Empregado tem um método para calcular bônus anual do empregado:
class Funcionario { 
 ComputeBonus dinheiro () {bonus / * padrão * /} 
 // Etc 
}
Subclasses diferentes de funcionários: Gerente, Programador, Secretário, etc podem querer substituir esse método para refletir o fato de que alguns tipos de empregados (gerentes) recebem bônus melhores que outros (secretários e programadores):
class Gerente extends Funcionario { 
 ComputeBonus dinheiro () {bonus / * generoso * /} 
 / / Etc 
}
29
Princípio do Reuso por composição
The Composite Reuse Principle (CRP)
Há vários problemas com esta solução.
Todos os programadores ganham o mesmo bônus. E se quiséssemos variar o cálculo do bônus entre os programadores? Será que precisamos introduzir uma subclasse especial de programador?
class ProgramadorSenior extends Programador { 
 ComputeBonus dinheiro () {bonus / * generoso * /} 
 / / Etc 
}
Note também que isto leva a duplicação de código.
E se quisermos mudar o cálculo do bônus para um funcionário específico? Por exemplo, se quisermos promover um Fulano de programador para programador sênior? Será que teríamos que recompilar todo o código?
30
Princípio do Reuso por composição
The Composite Reuse Principle (CRP)
Há vários problemas com esta solução.
E se nós decidimos dar a todos os programadores o mesmo bônus "generosos" que os gestores recebem? Que mudanças precisamos fazer? Devemos ter uma implementação específica para o "bônus generoso" ? Devemos copiar e colar o algoritmo "bônus generoso " do gerente para programador?
Uma solução seria usarmos interfaces:
31
Princípio do Reuso por composição
The Composite Reuse Principle (CRP)
Herança
Comportamento é herdado
Não podemos alterar o comportamento sem escrever mais código
Composição
Comportamento como um atributo
Mais flexibilidade
Permite alterar o comportamento em tempo de execução
32
Referências
33
Interface Segregation Principle - ISP, Engenharia de Software Centrada em Métodos Ágil, Thiago César Walter Brito - http://www.slideshare.net/tceufrasio/isp-the-interface-segregation-principle
http://viniciusquaiato.com/blog/srp-single-responsibility-principle/
http://edgarddavidson.com/?page_id=1871
http://www.slideshare.net/engenhariadesoftwareagil/princpio-law-of-demeter-lod
http://www.cs.sjsu.edu/~pearce/cs251b/principles/crp.htm
http://www.objectmentor.com/resources/articles/dip.pdf
http://www.objectmentor.com/resources/articles/isp.pdf
http://www.objectmentor.com/resources/articles/ocp.pdf
http://www.objectmentor.com/resources/articles/lsp.pdf
http://www.objectmentor.com/resources/articles/crp.pdf
33
Prof. Clayton Vieira Fraga Filho
site: www.claytonfraga.pro.br
e-mail: claytonfraga@gmail.com
COM10508 – Projeto de Sistemas de Software
Princípios de Projeto
34

Teste o Premium para desbloquear

Aproveite todos os benefícios por 3 dias sem pagar! 😉
Já tem cadastro?

Outros materiais