Prévia do material em texto
Engenharia de Software Moderna Módulo 2 - Projeto & Arquitetura Semana 1 - Princípios de Projeto Prof. Marco Tulio Valente "O problema mais fundamental em Ciência da Computação é a tarefa de decomposição de problemas: como dividir um problema complexo em partes que possam ser resolvidas de forma independente" -- John Ousterhout 2 Definição de Projeto de Software 3 ● Frase de Ousterhout é uma excelente definição ● Projeto: ○ Quebrar um "problema grande" em partes menores ○ Resolução (ou implementação) das partes menores resolvem (ou implementam) o "problema grande" Project vs Design 4 ● Em Inglês, temos duas palavras: ○ Project: esforço colaborativo para resolver problemas ○ Design: desenho ou proposta de uma solução ● Em Português, temos uma única palavra: projeto ● Neste módulo, projeto = design Projetar = Quebrar em partes menores 5 ● Exemplo: compilador Módulos 6 ● Em Engenharia de Software: ○ Partes menores = módulos (pacotes, componentes, classes, etc) Mais um conceito: Abstração 7 ● Conceito que permite "usar" um módulo sem conhecer detalhes de sua implementação ● Exemplo: scanner (analisador léxico) ○ Implementar um scanner pode ser difícil ○ Mas usar é simples: String token; token = Scanner.next_token(); O que vamos estudar? (parte 1) 8 ● Propriedades de "bons projetos" de software ○ Integridade Conceitual ○ Ocultamento de Informação ○ Coesão ○ Acoplamento O que vamos estudar? (parte 2) 9 ● Princípios (ou diretrizes) para projeto de módulos com as propriedades que estudamos antes: ○ Responsabilidade Única ○ Segregação de Interfaces ○ Prefira Interfaces a Classes ○ Aberto/Fechado ○ Demeter ○ Substituição de Liskov Fim 10 Propriedades de Projeto 11 Integridade Conceitual 12 Integridade Conceitual 13 ● Funcionalidades de um sistema devem ser coerentes ● Sistema não pode ser um "amontoado" de funcionalidades sem nenhuma coerência ou consistência Exemplos 14 ● Botão "sair" é idêntico em todas as telas ● Se um sistema usa tabelas para apresentar resultados, todas as tabelas têm o mesmo leiaute ● Todos os resultados são mostrados com 2 casas decimais Integridade Conceitual vale também para o projeto e código de um sistema 15 Exemplos (em nível de projeto/código) 16 ● Todas as variáveis seguem o mesmo padrão de nomes ○ contra-exemplo: nota_total vs notaMedia ● Todas as páginas usam o mesmo framework (mesma versão) ● Se um problema é resolvido com uma estrutura de dados X, todos os problemas parecidos também usam X Integridade Conceitual = coerência e padronização de funcionalidades, projeto e implementação 17 Exemplo Contra-Exemplo Integridade conceitual é a consideração mais importante no projeto de sistemas -- Fred Brooks 18 Motivo: integridade conceitual facilita uso e entendimento de um sistema 19 Ocultamento de Informação (Information Hiding) 20 Origem do conceito (David Parnas, 1972) 21 22 23 placa modelo 24 Construtora, cria a Hashtable 25 Problema: clientes precisam manipular uma estrutura de dados interna da classe, para estacionar um veículo Problema 26 ● Classes precisam de um pouco de "privacidade" ● Até para evoluir de forma independente dos clientes ● Código anterior: clientes manipulam a hashtable ● Comparação: clientes não podem entrar na cabine do estacionamento e eles mesmo anotar os dados do seu carro no "livro" do estacionamento Agora uma versão com ocultamento de informação 27 28 1 29 2 30 Resultado: classe Estacionamento fica livre para alterar a sua estrutura de dados interna 3 Ocultamento de Informação 31 ● Classes devem ocultar detalhes internos de sua implementação (usando modificador private) ● Principalmente aqueles sujeitos a mudanças ● Adicionalmente, interface da classe deve ser estável ● Interface = conjunto de métodos públicos de uma classe Fim 32 Coesão 33 Coesão 34 ● Uma classe deve ter uma única função, isto é, oferecer um único serviço ● Vale também para outras unidades: funções, métodos, pacotes, etc. Contra-exemplo 1 35 Contra-exemplo 1 36 Deveria ser quebrada em duas funções: sin e cos Contra-exemplo 2 37 Contra-exemplo 2 38 Deveria ser quebrada em duas classes: Estacionamento e Gerente Exemplo 39 Todos esses métodos manipulam os elementos da Pilha Acoplamento 40 Acoplamento 41 ● Nenhuma classe é uma ilha ... ● Classes dependem uma das outras (chamam métodos de outras classes, estendem outras classes, etc) ● A questão principal é a qualidade desse acoplamento ● Dois tipos: ○ Acoplamento aceitável ("bom") ○ Acoplamento ruim Acoplamento Aceitável 42 ● Classe A depende de uma classe B: ○ Mas a classe B possui uma interface estável ○ Classe A somente chama métodos da interface de B 43 Classe Estacionamento depende (está acoplada) à classe Hashtable, mas esse acoplamento é aceitável Acoplamento Ruim 44 ● Classe A depende de uma classe B: a. Mas interface da classe B é instável b. Ou então a dependência não ocorre via interface de B Como uma classe A pode depender de uma classe B sem ser via a interface de B? 45 46 "arq1.db" B A grava lê 47 "arq1.db" B A grava lê Problema 48 ● Classe B não sabe que a classe A é sua "cliente" ● Logo, B pode mudar o formato do arquivo ou mesmo deixar de salvar o dado que é lido por A "arq1.db" B A grava lê Tornando acoplamento ruim em bom 49 Tornando acoplamento ruim em bom 50 Fim 51 Princípios de Projeto 52 53 "Diretriz" Consequência (o que vamos ganhar seguindo o princípio) Princípios SOLID ● Single Responsibility Principle ● Open Closed/Principle ● Liskov Substitution Principle ● Interface Segregation Principle ● Dependency Inversion Principle 54 Robert Martin (1) Princípio da Responsabilidade Única 55 Princípios da Responsabilidade Única 56 ● Toda classe deve ter uma única responsabilidade ● Deve existir um único motivo para modificar uma classe 57 Responsabilidade #1: calcular índice de desistência 58 Responsabilidade #2: imprimir índice de desistência Agora versão com separação de responsabilidades 59 60 61 Uma única responsabilidade: interface com o usuário 62 Uma única responsabilidade: "lógica ou regra de negócio" Vantagens 63 ● Classe de negócio (Disciplina) pode ser usada por mais de uma classe de interface (Console, WebApp, MobileApp ...) ● Divisão de trabalho: ○ Classe de interface: frontend dev ○ Classe de negócio: backend dev (2) Princípio da Segregação de Interfaces 64 Segregação de Interfaces 65 ● Interfaces devem ser pequenas, coesas e específicas para cada tipo de cliente ● Caso particular do princípio anterior, mas voltado para interfaces 66 Interface genérica: trata de funcionários CLT e de funcionários públicos O que "getSIAPE" retorna para funcionários CLT? Agora versão que atende segregação de interfaces 67 68 69 Comum para todos funcionários 70 Específica para funcionários CLT 71 Específica para funcionários públicos (3) Princípio da Inversão de Dependências 72 Inversão de Dependências 73 ● Na verdade, vamos chamar esse princípio de "Prefira Interfaces a Classes" ● Pois transmite melhor a sua ideia! 74 75 Nos clientes, quando declarar variáveis ou parâmetros prefira sempre uma interface Ou seja, use I em vez de C1 ou C2 Por que? 76 ● Cliente funciona com qualquer classe que implementa I ● Isto é, com objetos das classes C1 e C2 ● E também com uma nova classe (por exemplo, C3) que venha a ser criada Exemplo 77 Cliente sendo instanciado com objeto da classe C1 Exemplo 78 Objeto da mesma classe (Cliente), mas instanciado com objeto do tipo C2 Fim 79 (4) Prefira Composição a Herança 80 Contexto Histórico 81 ● Na década de 80, quando orientação a objetos tornou-se popular, as pessoas começaram a "abusar" de herança ● Achavam que herança iria ser umabala de prata, promover reuso em larga escala, etc. Herança 82 ● Relação "é-um" ● Exemplo: MotorGasolina é-um Motor ● No código: class MotorGasolina extends Motor { ... // herda atributos e métodos de motor } Composição 83 ● Relação "possui" ● Exemplo: Painel possui ContaGiros ● No código: class Painel { ContaGiros cg; // possui um atributo ... } Prefira Composição a Herança ⇒ não force o uso de herança 84 Uso "forçado" de herança 85 Herança Uso "forçado" de herança 86 em vez de: Herança Composição (5) Princípio de Demeter 87 Demeter 88 ● Demeter era o nome de um grupo de pesquisa de uma universidade norte-americana ● Evite longas "cadeias" de chamadas de métodos ● Exemplo: obj.getA().getB().getC().getD().getOqueEuPreciso(); objetos de passagem Motivo 89 ● Longas cadeias de chamadas quebram "encapsulamento" ● Não quero passar por A, B, C, D até obter que eu preciso ● Elos intermediários tornam a chamada frágil 90 Define quais chamadas de métodos são "permitidas" no corpo de um método 91 92 93 94 Fim 95 (6) Princípio Aberto/Fechado 96 Princípio Aberto/Fechado 97 ● Proposto por Bertrand Meyer ● Ideia: uma classe deve estar fechada para modificações, mas aberta para extensões Explicando melhor 98 ● Suponha que você vai implementar uma classe ● Usuários ou clientes vão querer usar a classe (óbvio!) ● Mas vão querer também customizar, parametrizar, configurar, flexibilizar e estender a classe! ● Você deve se antecipar e tornar possível tais extensões ● Mas sem que os clientes tenham que alterar o código da classe Como tornar uma classe aberta a extensões, mas mantendo o seu código fechado para modificações? 99 ● Parâmetros ● Funções de mais alta ordem ● Padrões de projeto ● Herança ● etc Exemplo 100 Ordena a lista passada com parâmetro Exemplo 101 Ordena uma lista passada com parâmetro Mas agora eu quero ordenar as strings da lista pelo seu tamanho, isto é, pelo número de chars 102 Será que o método sort está aberto (preparado) para permitir essa extensão? Mas, mantendo o seu código fechado, isto é, sem ter que mexer no seu código 103 Felizmente, sim! 104 lista de strings Objeto com um método compare, que vai comparar duas strings. Não existe almoço grátis, cliente tem que implementar esse método Resumindo: ao implementar uma classe, pense em pontos de extensão! 105 (7) Princípio de Substituição de Liskov 106 Princípio de Substituição de Liskov 107 ● Nome é uma referência à Profa. Barbara Liskov ● Princípio define boas práticas para uso de herança ● Especificamente, boas práticas para redefinição de métodos em subclasses Primeiro: vamos entender o termo "substituição" 108 109 110 111 ● Tipo A pode ser substituído por B1, B2, B3,... ● Desde que eles sejam subclasses de A ● Em tempo de execução, método g chamado vai ser aquele de B1, B2, B3, etc. Princípio de Substituição de Liskov 112 ● Redefinições de métodos em subclasses são possíveis ● Mas devem preservar o contrato do método da superclasse ● Preservar o contrato: tanto faz chamar A.g ou B1.g ou B2.g ou B3.g Para concluir, vou dar um exemplo do dia-a-dia 113 ● Suponha um Médico A plantonista em um hospital ● Em um fim de semana, ele não poderá fazer seu plantão ● Então, ele pede para um colega B1 substituí-lo ● Quando a substituição vai funcionar? ○ Quando B1 tiver pelo menos a mesma competência de A ○ A substituição não vai afetar o funcionamento do hospital ○ Substituição de Liskov 114 ● Quando a substituição não vai funcionar? ○ Exemplo: quando A for um Clínico Geral e B1 um Pediatra ○ Essa substituição vai prejudicar o funcionamento do hospital 115 Fim 116