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