Mais complexidade

Ouvi uma expressão que, para se tornar um programador, você precisa ser preguiçoso. Mas, às vezes, a preguiça na programação leva a uma terrível dívida técnica. No meu artigo sobre SRP, mencionei que violar esse princípio pode levar a um aumento na complexidade ou mesmo à sua multiplicação. Um de meus colegas produziu um exemplo interessante e, com minha ajuda, decidi demonstrar como fica.



Vamos decidir o que é essa complexidade excessiva. Mas primeiro, vamos falar sobre o contrário, sobre a complexidade dos requisitos. Por exemplo, existem requisitos para calcular o salário do funcionário a partir da taxa horária e das horas trabalhadas. E, se um funcionário estiver trabalhando na empresa há mais de cinco anos, acumule um bônus. Esse "se" vem dos requisitos e não pode ser evitado. De uma forma ou de outra, ele se tornará um elemento de complexidade no código do aplicativo, provavelmente na forma de um operador condicional "se". Mas, às vezes, a complexidade não procede dos requisitos, mas segue da abordagem do desenvolvedor para resolver o problema.

O operador “if”, padrões como “estratégia”, métodos polimórficos não são uma lista completa de técnicas de programação que podem conter essa complexidade excessiva. Pessoalmente, eu, a propósito, sou sempre contra o uso de padrões pelos desenvolvedores simplesmente porque eles podem, e não para resolver um problema específico.

Aqui está um exemplo simples. Pode parecer fictício, mas não é. Nem sequer é simplificado, foi nesta forma que eu o conheci durante uma revisão de código há alguns anos. Em dois lugares no código, houve chamadas para a mesma função, mas com um parâmetro booleano diferente:

// first place doSomething(true); // second place doSomething(false); 

Projetos similares sempre parecem suspeitos e essa função não me decepcionou. Este parâmetro foi passado com o único objetivo de ser verificado dentro desta função:

 doSomething(flag: boolean): void { if(flag) { // do first thing } else { // do second thing } } 

Essa verificação pode ser descrita como "se fui chamado do local A, fazemos uma coisa, caso contrário, fui chamado do local B, fazemos outra coisa". Essa bandeira, esse "se" é o motivo de toda essa nota. A complexidade não vem dos requisitos de negócios. Naturalmente, eu recomendo alterar o código da seguinte maneira:

 // first place doFirstThing(); // second place doSecondThing(); //method is split into 2 parts each having their own responsibility doFirstThing(): void { // do first thing } doSecondThing(): void { // do second thing } 

Isso é tudo, não há mais complexidade. É aqui que o desenvolvedor não deve ser muito preguiçoso e escrever outra assinatura de função.

Aqui você pode exclamar: “Mas este é apenas um 'se'”, ou: “Essa violação é óbvia, quem escreve o código assim?” E aqui vem o segundo exemplo. Isso mostra que pode ser visivelmente mais difícil ver a violação e também que o custo dessa violação pode ser mais do que apenas um "se". Como no primeiro exemplo, a função é usada em dois lugares:

 // first place checkValidity(obj); // second place checkValidity(arrayOfObjs); 

O método, como segue seu nome, verifica a validade do objeto. No entanto, não era óbvio que ele também pudesse verificar a validade de uma matriz de objetos. Ajustei os nomes das variáveis ​​para enfatizar essa violação. O método fica assim:

 checkValidity(parm: MyType | MyType[]): void { if(Array.isArray(parm)) { parm.forEach(p => checkValidity(p)); } else { // here the object gets checked // and conditional exception is thrown } } 

Aqui está. Um se torna muitos ifs. Se a matriz contiver 100 objetos, esse "se" será executado 101 vezes. E em dados reais, poderíamos ter 30 mil objetos lá, e isso já é uma perda de desempenho impressionante.

Obviamente, seguindo o princípio da responsabilidade exclusiva, esse método precisa ser refatorado para que dois métodos sejam obtidos:

 checkItemsValidity(parms: MyType[]): void { parms.forEach(p => checkItemValidity(p)); } checkItemValidity(parm: MyType): void { // here the object gets checked // and conditional exception is thrown } 

Da mesma forma, você também precisa ajustar os pontos de chamada.

É interessante que os exemplos que forneci no artigo sobre SRP levaram a um aumento no SLOC; os mesmos exemplos, pelo contrário, levaram a uma ligeira diminuição, juntamente com a melhoria esperada na qualidade do código.

Isso é tudo. Apenas alguns exemplos simples para demonstrar o princípio mais importante do bom código.

Source: https://habr.com/ru/post/pt457018/


All Articles