Baixe o app para aproveitar ainda mais
Prévia do material em texto
TP2 Engenharia de Software Disciplina: Melhores práticas de codificação Professor: Luiz Paulo Maia Aluno: João Pedro Silva Caglianone 2 1. Cite três exemplos de sinais de problemas no código que podem indicar a necessidade de refatoração e que sejam boas práticas de código limpo. ● Código Duplicado - quando se nota trechos de código muito semelhantes ou idênticos em diferentes partes do seu projeto, devendo abstrair as repetições para evitar bugs e inconsistências. ● Métodos ou Classes Longos - métodos (funções) ou classes que são muito longos geralmente são um sinal de que eles estão fazendo mais do que deveriam. Idealmente, cada método ou classe deve ter uma única responsabilidade clara. ● Alto Acoplamento e Baixa Coesão - O acoplamento se refere à interdependência entre módulos ou classes, enquanto a coesão se refere a quão bem os elementos internos de um módulo ou classe estão relacionados entre si. Código com alto acoplamento e baixa coesão pode ser difícil de manter e de testar, logo, refatorá-lo afim de reduzir o acoplamento e aumentar a coesão é uma boa prática de refatoração. 2.Quais são os problemas decorrentes da utilização de nomes sem significados dentro de um código? Utilizar nomes sem significado em um código pode causar uma variedade de problemas, afetando desde a compreensão até a manutenção do software. Quando os nomes no código não são claros ou descritivos, o entendimento do que cada parte faz se torna muito complicado. A falta de clareza nos nomes também aumenta o tempo necessário para manutenção, pois os desenvolvedores precisam gastar tempo extra analisando o código para decifrar seu propósito e funcionamento. 3 Além disso, a utilização de nomes genéricos ou sem significado eleva o risco de erros e bugs, pois pode haver confusões sobre a função exata de uma variável ou método. Essa ambiguidade torna o processo de refatoração e expansão do código mais complicado e arriscado, pois a incerteza em relação ao papel de cada elemento do código pode levar a hesitação em realizar mudanças necessárias. Em um ambiente de equipe, onde a revisão de código é uma prática comum para assegurar a qualidade, nomes não descritivos tornam esse processo mais difícil e demorado. Revisores podem ter dificuldades em entender a intenção do código, o que compromete a eficácia das revisões. 3. Como identificar métodos longos, e como podemos fazer a refatoração desses métodos? Identificar e refatorar métodos longos é uma tarefa importante para manter a qualidade do código. Métodos longos tendem a fazer muitas coisas ao mesmo tempo, resultando em um código difícil de ler, entender e manter. Geralmente, é possível identificar um método longo pelo seu tamanho excessivo, muitos níveis de aninhamento, e a execução de múltiplas operações diferentes. Para refatorar um método longo, é essencial entender completamente o que o método faz. Em seguida, é preciso identificar partes do método que realizam tarefas específicas e independentes. Cada uma dessas partes pode ser extraída para formar um novo método, com um nome que claramente descreva sua função. Este processo, conhecido como "extrair método", ajuda a tornar o código mais modular, legível e fácil de manter. Após a extração, é importante verificar se os novos métodos podem ser aplicados em outras partes do código, promovendo a reutilização e reduzindo a duplicação. Por fim, é crucial testar o código refatorado para garantir que nenhuma funcionalidade foi perdida ou erro introduzido durante o processo. 4 4. Como o polimorfismo pode substituir comandos de decisão (switch ou if-else) quando tratamos diferentes comportamentos dentro de uma classe? O polimorfismo, um conceito fundamental da programação orientada a objetos, permite substituir comandos de decisão como switch ou if-else ao tratar diferentes comportamentos dentro de uma classe. Essencialmente, ele permite que objetos de diferentes classes sejam tratados através de uma interface comum. Quando usamos o polimorfismo, definimos uma interface ou classe base comum que declara métodos que serão implementados de maneira diferente por suas subclasses. Cada subclasse representa uma variação de comportamento, implementando os métodos de acordo com suas necessidades específicas. Em vez de usar um comando switch ou cadeias de if-else para selecionar comportamentos com base no tipo de objeto, simplesmente chamamos o método relevante sobre um objeto. Em tempo de execução, o sistema de tipos da linguagem de programação assegura que o método correto seja chamado, de acordo com a classe real do objeto. Isso é conhecido como ligação dinâmica ou ligação tardia. 5 5. Como os comentários podem significar sinais de problemas no código? ● Código Complexo ou Confuso: Se um código requer muitos comentários para ser entendido, isso pode indicar que o próprio código é complexo ou confuso. Idealmente, o código deve ser autoexplicativo, com nomes claros para variáveis, métodos e classes, e uma estrutura que facilite a compreensão. ● Desatualização: Comentários podem se tornar desatualizados à medida que o código evolui. Comentários desatualizados podem ser piores do que nenhum comentário, pois podem induzir ao erro, fornecendo informações incorretas sobre o que o código faz. ● Dependência excessiva de comentários: Um código bem escrito deve ser relativamente claro por si só. Se um desenvolvedor sente a necessidade de comentar excessivamente, isso pode indicar uma falta de confiança na clareza do código. Isso pode ser um sinal de que o código precisa ser reestruturado ou escrito de forma mais clara. ● Indicador de código morto: Comentários que indicam porções de código que foram desativadas (código morto) podem sinalizar que há partes desnecessárias no código. Essas seções devem ser avaliadas para remoção ou refatoração, pois o código morto pode levar à confusão e a problemas de manutenção. 6 6. Quais são os problemas decorrentes de códigos duplicados, e como podemos resolvê-los? Problemas : ● Manutenção Mais Difícil: Se a mesma lógica está duplicada em várias partes do código, qualquer mudança nessa lógica exige alterações em todos os locais onde está duplicada, tornando a manutenção mais complexa e propensa a erros. ● Inconsistências e Bugs: Se um desenvolvedor atualiza a lógica em um local, mas esquece de fazer o mesmo em outro, isso pode levar a inconsistências e bugs. ● Dificuldade na compreensão do código: Códigos duplicados podem tornar o projeto mais difícil de entender, pois o mesmo conceito ou lógica é repetido em vários lugares, aumentando a complexidade geral do código. ● Inchaço do código: A duplicação aumenta o tamanho do código desnecessariamente, o que pode afetar a legibilidade e a performance em alguns casos. Soluções : ● Abstração em funções ou métodos: Quando a mesma lógica é usada em várias partes do código, pode-se abstrair essa lógica em uma função ou método único. Isso permite a reutilização do código e facilita a manutenção. ● Padrões de projeto: Certos padrões de projeto, como Template Method ou Strategy, podem ser usados para reduzir a duplicação de código, especialmente quando a duplicação ocorre em classes diferentes. ● Herança e composição: Na programação orientada a objetos, a herança e a composição podem ser usadas para compartilhar código comum. ● Refatoração regular: Fazer refatorações regulares como parte do processo de desenvolvimento pode ajudar a identificar e eliminar duplicações à medida que o código evolui. 7 ● Utilização de Bibliotecas: Muitas vezes, o código duplicado pode ser substituído por funções disponíveis em bibliotecas padrão ou de terceiros. 7. Quais são os problemas decorrentes do uso de longa lista de parâmetros e como podemos resolvê-los? Problemas ● Dificuldade de compreensão: Métodos com muitos parâmetros são difíceis de entender. Com uma longa lista, fica complicado lembrar a ordem e o propósito de cada parâmetros. ● Manutençãocomplexa: Adicionar, remover ou alterar parâmetros torna-se mais complexo e propenso a erros. ● Risco de erros: Com muitos parâmetros, aumenta o risco de passar argumentos na ordem errada, especialmente se eles forem do mesmo tipo. ● Testabilidade reduzida: Testar um método que requer muitos parâmetros pode ser trabalhoso, pois você precisa considerar uma grande variedade de combinações de entradas. Soluções : ● Objeto de parâmetro: Em vez de múltiplos parâmetros, use um objeto que agrupe todos os dados necessários. Isso reduz o número de parâmetros e torna o código mais legível. ● Padrão builder: Para construção de objetos complexos, o padrão Builder oferece uma alternativa legível e flexível. Ele permite a configuração incremental de um objeto, evitando construtores com muitos parâmetros. ● Métodos auxiliares: Em algumas situações, você pode dividir um método grande em vários métodos menores, cada um lidando com uma parte dos dados. 8 ● Parâmetros padrão e sobrecarga de métodos: Em linguagens que suportam, usar parâmetros padrão ou sobrecarregar métodos pode ajudar a reduzir a quantidade de parâmetros necessários em cada chamada. ● Agrupar parâmetros relacionados: Se alguns parâmetros estão sempre juntos, eles podem ser agrupados em uma nova classe ou estrutura. 8. Quais são os problemas decorrentes da utilização de dados globais e como podemos resolvê-los? Problemas: ● Acoplamento: Dados globais criam um alto grau de acoplamento entre diferentes partes do código. Múltiplas funções ou classes podem depender de uma variável global, tornando o sistema mais interconectado e frágil. ● Dificuldade de rastreamento e depuração: Quando várias partes do programa podem alterar um dado global, fica difícil rastrear quem e quando modificou o dado, o que complica a depuração. ● Problemas de concorrência: Em ambientes de programação concorrente, o acesso e a modificação de dados globais podem levar a condições de corrida, tornando o código instável e imprevisível. ● Testabilidade reduzida: Testar partes do código que dependem de dados globais pode ser desafiador, pois o estado desses dados precisa ser gerenciado cuidadosamente entre os testes. ● Dificuldade na manutenção e na escalabilidade: À medida que o sistema cresce, o uso de dados globais pode se tornar um empecilho para a manutenção e a expansão do código. Soluções: ● Encapsulamento: Em vez de usar dados globais, encapsule esses dados dentro de classes ou módulos. Isso limita o acesso e a modificação 9 dos dados a métodos específicos, controlando como os dados são acessados e alterados. ● Injeção de Dependência: Passe os dados necessários como parâmetros ou através de mecanismos de injeção de dependência. Isso aumenta a flexibilidade e a testabilidade do código e reduz o acoplamento. ● Padrões de design: Utilize padrões de design como Singleton (com cautela) ou Service Locator para controlar o acesso a recursos compartilhados, controlando como e quando os dados são acessados. ● Variáveis de instância: Converta variáveis globais em variáveis de instância de classes. Isso as torna parte do estado de um objeto, ao invés de serem acessíveis globalmente. ● Contextos ou configurações específicas: Em alguns casos, criar objetos de contexto ou configuração que são passados através das chamadas de função pode ser uma solução eficaz. ● Controle de concorrência: Implemente mecanismos de controle de concorrência, como locks ou semáforos, para gerenciar o acesso a dados compartilhados em ambientes multithread. 9. Como identificar classes longas e como podemos fazer a refatoração dessas classes? Identificação de classes longas: ● Tamanho físico: Uma classe que possui um grande número de linhas de código é um sinal óbvio. Não existe um limite exato, mas uma classe com centenas de linhas pode ser um candidato para refatoração. ● Múltiplas responsabilidades: Se uma classe realiza múltiplas funções distintas, como manipulação de dados, lógica de negócios e interações de interface do usuário, ela provavelmente faz mais do que deveria. ● Alto número de métodos e variáveis: Um grande número de métodos e variáveis pode indicar que a classe tem muitas responsabilidades. 10 ● Alto acoplamento: Se a classe tem dependências extensas com outras classes, pode ser um sinal que ela faz um trabalho que deveria ser delegado. Refatoração : ● Identifique responsabilidades: Analise a classe para entender suas diferentes responsabilidades. O objetivo é identificar segmentos de código que podem ser agrupados logicamente. ● Extrair classes: Crie novas classes para cada responsabilidade identificada. Exemplo, se uma parte do código está lidando especificamente com acesso a dados, mova essa lógica para uma nova classe de repositório. ● Extrair métodos: Se houver métodos longos dentro da classe, considere dividi-los em métodos menores, cada um com uma única responsabilidade. ● Aplicar padrões de design: Utilize padrões de design apropriados para ajudar na organização do código. Por exemplo, o padrão Strategy pode ser usado para encapsular algoritmos variáveis, e o padrão Observer pode ser útil para gerenciar comunicações complexas entre classes. ● Revisar relações com outras classes: Examine como a classe interage com outras classes. Injeção de dependência reduz o acoplamento. ● Testes unitários: Depois de refatorar, é crucial escrever ou atualizar testes unitários para garantir que o novo código funcione conforme esperado e para prevenir regressões.
Compartilhar