Buscar

TP2_Refatoracao

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.

Continue navegando