No desenvolvimento de software, nós desenvolvedores sempre presamos por construir sistema que sejam o mais fiel possível ao pedido do cliente ( por mais que haja mudanças no decorrer do tempo ) , mas além de tudo presamos por outros fatores que acontecem no decorrer ou após o desenvolvimento.
Mudanças
Talvez seja um dos principais motivos para focarmos em um bom desenvolvimento de softwares: poder adaptar à mudanças que possam ocorrer nas regras de negócios ( o que é totalmente plausível ) . Um dos princípios SOLID fala justamente nesse fator, poder construir um software que quando for solicitado alguma mudança não seja alterado o código existente, seja adicionado algo novo para poder suprir essa mudança que ocorreu.
Entrada de um novo desenvolvedor
Em toda a equipe de desenvolvedores com certeza poderá haver a chegada de um novo integrante , e tem vez que um software mal escrito pode causar até uma demora na adaptação desse novo integrante na equipe, visto que não entenderá muito bem o que determinada classe faz no sistema como um todo ou até ficar um mal entendido na função dela e continuar o desenvolvimento com algum tipo de erro.
Manutenção
A maioria dos softwares precisam em algum momento de sua existência de uma manutenção , seja para consertar algum bug que apareceu ou pra ajustar algo que não se adaptou bem na sua instalação no local onde ficaria instalado, e os SOLID facilitariam isso pois o sistema estaria estruturado de uma maneira que seria mais fácil ( ou menos dolorido ) encontrar o bug que deu origem à manutenção do software , pois de certa forma o próprio software “falaria” qual o tipo de erro que ocorreu , e não uma forma genérica; por exemplo , uma exception levantada do tipo RuntimeException
pois assim não diria muita coisa sobre o erro, para facilitar teria várias mensagens customizadas de exceções que podem ter ocorrido durante o erro ( mensagens de regras de negócio ) e assim ir direto ao tipo do erro e poder solucioná-lo.
Observação: Sobre existir manutenção em softwares: Por que após mais de 40 anos de experiência em desenvolvimento de software e milhares de livros feitos na área de Engenharia de Software sobre formas de conduzir desenvolvimentos de softwares corretos, ainda existe softwares com falhas nos negócios e que precisam de manutenção? Teoricamente não era pra haver mais softwares com manutenção devido ao tempo de “prática” na área..
Bom , passado a parte do que um bom código escrito usando os SOLID resolveria, vamos falar dos “bad smell” (Mau cheiro) , termo cunhado por Robert C. Martin - Uncle Bob, para falar de uma forma humorada dos “cheiros“ que um código mal escrito “exala” .
Exemplos de Bad Smell
Rigidez
Esse mau cheiro diz respeito ao quanto o software é difícil crescer ou mudar , quando ele é muito amarrado a implementações de outras partes do software , ou seja , possui um alto acoplamento com outras classes, em que se mudar algo em algumas das suas dependências haverá algum efeito colateral na classe que a usa. Exemplos:
- Efeitos colaterais internos ao mudar implementação das suas dependências.
- Uso exagerado de herança, onde mudando na classe mãe, mudará necessariamente nas suas filhas, causando uma cascata de alterações involuntárias.
- Pensar : “É muito mais difícil do que eu pensava” kkk . Faz parte.
Viscosidade
Diz respeito à facilidade que é fazer primeiramente o que é errado do que o certo em desenvolvimento de software, quando você tem uma funcionalidade pra implementar pensa primeiro em resolver o determinado problema do que abstraí-lo e pensar mais abstratamente podendo prever futuros problemas que podem acontecer, e pensando em abstrações você pode até tornar seu software adaptável à mudanças que venham a ocorrer, claro , não exagerar em abstrações, pois pode ser que haja por exemplo uma abstração e somente uma classe use ela.
Nesse tópico entra a parte dos testes, que é uma das maiores resistências dos desenvolvedores, fazendo os testes do software , TDD , você modela como seu software funcionará e ainda estará testando diversos cenários onde seu software atuará, facilitando assim refatorações futuras, pois estará apoiado nos testes quando mudar determinada implementação, não tendo toda hora que testar input após input do seu sistema, o que geralmente torna o teste manual do sistema custoso.
Repetição Desnecessária
Nesse mau cheiro há o clássico copy-paste ( copiar - colar ) , quando ocorre o uso de muito ctrl+c + ctrl+v , copia a funcionalidade de um método e cola em outro porque tem um função parecida, ou se não houver um código bem encapsulado poderá ter um implementação solta no código e uma vez ou outra ter que usar em outro local, e como não está encapsulado ocorrer o mau cheiro da rigidez, não podendo reutilizar o método ( funcionalidade ) em outro local, e assim, causando repetições desnecessárias, exemplos:
- Uso de ctrl+f ( procurar ) para encontrar os diversos lugares em que existe determinada implementação quando for mudá-la.
- Uso de ctrl+c + ctrl+v em vários lugares.
- Uso exagerado de perguntas à objetos de qual tipo ou sobre determinada informação em que se está interessado , por exemplo : perguntar a função de determinado objeto funcionário para aplicar determinado desconto no seu salário, o que poderia ser resolvido com polimorfismo.
Objetos devem falar o que querem fazer e não perguntar aos outros se eles estão aptos a resolver seus problemas. Objects should tell , don't ask.
Copiar / Colar em editores de textos é aceitável, mas em desenvolvimento de software pode ser catastrófico.
Complexidade Desnecessária
Esse mau cheiro é característico da “melhora antecipada” , pois o desenvolvedor começa já aplicando diversas implementações no código para poder ter um melhor aproveitamento de performance no código , o mais clássico que vejo é: “Um for de determinado jeito é mais rápido?”, ele começa antecipar ocasiões futuras que podem nunca ocorrer, como suportar milhões de usuários no sistema dele, no caso de desempenho, tem também a parte de criar abstrações desnecessárias, já começa a aplicar padrões de projeto nas primeiras linhas do código , sendo que eles servem para fazer refatorações em códigos já bem estruturados , com Padrões de Projeto no início causa uma complexidade antecipada que pode custar um design bem feito do software. Exemplos.
- Usando operadores ternários com 3 ou 4 verificações, por exemplo, PHP;
- Usando funções
lambda
com vários argumentos e ainda adiciona uns map’s , filter’s .. tudo inline ainda por cima, por exemplo , Python; - Operadores bitwise com vários shifts e usando números mágicos para essas manipulações pra lá e pra cá, depois a pessoa nem lembra pra que servia o número.
Uma dica: quando ver um mau cheiro forte espere um tempo até consertá-lo, por exemplo nas abstrações, espere aparecer um caso em que precise para poder criá-las. Pois pode ficar um outro mau cheiro : complexidade desnecessária.
“Permita-se levar o primeiro tipo“ , após ele analise o problema e proponha uma solução.
No caso dos exemplos citado, prova que não é a ferramenta que influencia o programador a fazer a coisa errada, e sim o uso exagerado e exacerbado de determinadas funções da linguagem , e até o seu desconhecimento de parte das funcionalidades da linguagem, fazendo o programador não utilizar as melhores práticas e o que é recomendado .
Opacidade
Esse mau cheiro é clássico de código mau escrito, porque fala a respeito do quanto o próprio desenvolvedor não consegue mais ler seu próprio código , e assim se nem ele sabe explicar fica difícil .. :D . Esse mau cheiro se relaciona com o da viscosidade, pois quando mais fácil o desenvolvedor se inclinar em fazer a implementação imediatista que nem sempre é a melhor e mais completa , fica mais fácil ele fazer uma solução que fique um tanto implícita, que fique difícil de entender, por exemplo:
- Nomear variáveis que não representam muito o seu significado , por exemplo: a, pg, num… e por ai vai.
- Uso de magic strings para fazer representações de elementos no código , por exemplo: PG , NPG , AGNDD , para representar os status de uma compra ( PAGO , NÃO-PAGO , AGENDADO ) , além de magic numbers para o mesmo propósito: 0, 1, 2, 3 …
- Uso exagerado de comentários, onde o próprio código já teria que ser explicativo o bastante para dizer o que ele está fazendo. Existência de muitos comentários pode ser um mau indício.
Fragilidade
A fragilidade diz respeito à fraqueza do design do software , em que uma pequena mudança em uma determinada parte do código causa diversas mudanças em outras partes , o pior , partes que não estão nem relacionadas, os desenvolvedores podem nem saber as conexões que existem ao longo das classes. Exemplos :
- Dizem : “Nem cheguem perto ou mexam nessa classe”, tipo uma classe “god” que faz várias coisas , tipo amarra o sistema pra não cair.
- Manda o estagiário revisar determinada parte do código , fazer uma refatoração, pois dará muitos problemas, deixar ele um pouco lá resolvendo :D .
- Desenvolvedores tem medo de mexer em determinada parte do código por receio de causar vários problemas em cascata no resto do sistema.
A diferença com a rigidez é que aqui, por mais pequena que seja a mudança causa vários efeitos colaterais, já na rigidez, o desenvolvedor não pode mudar nada pois está tudo muito acoplado.
Imobilidade
Diz respeito ao alto acoplamento dos diversos componentes do sistema, com esse mau cheiro fica muito difícil decompor o sistema em módulos reutilizáveis , uma determinada função em uma classe poderia ser decomposta em uma classe e ser reutilizável no sistema como um todo, mas essa determinada função está intimamente ligada à implementação de uma parte do sistema, ficando impedido de retirá-la de lá e poder desacoplá-la e abstrair. Veja que relaciona com outros bad smells , por exemplo , a rigidez , onde o design do software está muito amarrado à implementações. Quando mais bad smells tiver, mais “estragado” estará o seu sistema :D . Exemplos:
- Implementações intimamente ligadas a componentes, dificultando o desacoplamento, abstração.