6 maneiras de fazer a mesma coisa, o que é considerado boas práticas?

As vezes tem tantas maneiras diferentes de fazer o mesmo código que nós ficamos na dúvida quanto a qual maneira usar. O que seria considerado "boa prática" pela comunidade e o que sua equipe entenderia melhor. Suponhamos que você esteja trabalhando dentro de um método de um Domain Service chamado UmDomainServiceChique(objetoDoDominio) que será chamado por uma API. Você tem uma regra de negócio chique para ser verificada que por enquanto chamarei de VerificaMinhaRegraChiqueComplexa(). Você chama UmDomainServiceChique(objetoDoDominio) e caso VerificaMinhaRegraChiqueComplexa() retorne true você vai querer que UmDomainServiceChique faça o que tem que fazer e a api retornar Ok 200, caso contrário você quer que a API responda um erro qualquer, tipo BadRequest, e retornar uma mensagem dizendo que VerificaMinhaRegraChiqueComplexa deu ruim. Eu vejo 6 maneiras de fazer isso, gostaria de saber a opinião de outrs devs sobre qual seria a maneira menos gambiarr

Até que ponto os softwares modernos necessitam de hardware?

Isso é apenas uma coisinha boba, mas fundamenta meu argumento:
Sempre achei que os softwares modernos estão ficando cada vez mais exagerados no que eles exigem de hardware, e acredito que é porque estão ficando cada vez mais mal-feitos.

Acredito que programadores sem um conhecimento técnico muito grande, iniciantes ou medianos podem tornar um programa mais lento e exigente a medida que ele faz mal uso do processador e da memória.
Para exemplificar isso criei um cenário onde poderíamos codificar um programinha.
Se eu tenho uma função que subtrai um valor de um determinado montante, mas o resultado não pode ser negativo, porque essa função alimentará uma outra que aceita como entrada apenas números naturais não negativos. Caso o valor seja maior que o montante a função deve retornar zero.
Imagine uma operadora de celular pré-pago. Ela deve cortar a ligação do usuário quando os créditos chegam a zero, e impedir que faça novas ligações, mas jamais permitir que os créditos do cliente fiquem negativos. Mesmo que o cliente tenha suponhamos 50 centavos e a ligação que ele fará custe 55, mesmo que a operadora resolva deixar o usuário completar a ligação, arredondando para menos e arcando com o onus de 5 centavos, o cliente nunca deverá ficar com saldo negativo (pode até não ser assim, mas estamos impondo isso como premissa).
Se o cliente tem um saldo de 50 e é debitado 55, o saldo deve ser 0. Todo saldo negativo deve ser 0.
Consideraremos apenas a função de débito, mas ela não deve nem ser executada caso o cliente não disponha realmente dos meios para pagar o serviço. Por exemplo se numa verificação de saldo descobre-se que o cliente tem 20 centavos e a ligação é 50, simplesmente ele não fará a ligação.
O mesmo vale para sites onde o conteudo visualizado é pré-pago através de créditos. Imagine que o cliente compra 50 creditos, usa um, se arrepende e pede o dinheiro de volta. Você deve extornar o dinheiro do cliente e zerar o seu saldo de créditos, debitando 50 (o que ele comprou) dos 49 (o que ele tem), porém sem deixa-lo com saldo negativo -1. Ele não irá gostar de, caso um dia compre créditos de novo, comprar 50 e visualizar 49, como se 1 tivesse sumido.
Veja as várias formas de se fazer isso:



//desperdiçando memória com variáveis desnecessárias:
function debita1(montante,num: integer): integer;
var temp: integer;
begin
 temp := montante-num;
 if temp < 0 then
  result := 0
 else
  result := temp;
end;

//desperdiçando processamento
function debita2(montante,num: integer): integer;
begin
 if montante-num < 0 then
  result := 0
 else
  result := montante-num;
end;

//usando um if
function debita3(montante,num: integer): integer;
begin
 if num > montante then
  result := 0
 else
  result := montante-num;
end;

//uma maneira matemática e elegante, arregaçando o processador
function debita4(montante,num: integer): integer;
begin
 result := (montante-num + trunc(sqrt(sqr(montante-num)))) div 2;
end;


//método "me acho o bom mas sou um mané"
function debita5(montante,num: integer): integer;
asm
  sub eax, edx
  jns @@final
  and eax, 0
  @@final:
end;




A função debita1 claramente está usando uma variável temporária desnecessária, uma vez que em certas condições o cálculo não será efetuado,


a função debita2 economiza memoria em relação a debita1, mas executa o cálculo duas vezes, uma no if.

a função debita3 seria a mais performática e correta.

a função debita4 parece bonita, usa a matemática, com raiz quadrada do quadrado de um número, para tirar modulo, a formula ((m-d)+|m-d|)/2, não tem nenhum if, mas é a pior de todas. ela usa os operadores sqr e sqrt, uma vez cada um, que são operadores para se trabalhar com ponto flutuante. O todo desta função, inclusive as funções sqrt e sqr, acabam gerando mais instruções de máquina do que as precedentes.

A função debita5 foi o resultado de várias tentativas de fazer uma função mais performática que a debita3 em assembly. Não consegui. Mesmo para um loop de cem bilhões de vezes as funções debita3 e debita5 têm performances semelhantes.

Se alguem tiver uma sugestão de uma função mais performática que a debita5 eu agradeço!

Lógico que nem sempre é assim. As vezes uma função com operações matemáticas é mais rápida do que um complexo labirinto de if's.
Num outro exemplo, se seus números de ponto flutuante não tem mais de 2 casas depois da vírgula, pode-se evitar se trabalhar com números decimais, que são mais lentos, e multiplicar todas as parcelas por 100, dividindo-as por 100 depois. Isso num loop muito grande economizaria bastante tempo do processador.
Parece besteira minha, mas cada ano que passa os softwares estão ficando mais exigentes em termos de hardware, para fazer as tarefas cada vez mais triviais. Um messenger moderno, que tem a função apenas de chat com outros messengers, não funciona num 486 ou pentium 100 por exemplo.
A maioria das linguagens gerenciadas exige que você tenha 1 gb de memória para executar um hello world.
Acredito que grande parte desse desperdício pode ser atribuido a desatenção dos programadores, e uma parte menor a burrice mesmo.

Só que considerando software cada vez mais exigente. Hardware cada vez mais exigindo energia (uma simples placa pci express off board te obriga a ter uma fonte de 450 watts reais no mínimo), cada vez mais computadores velhos sendo jogados no lixo, com seus chips de silício, que é um veneno para a natureza, não deveríamos nós tentar o máximo possível fazer um código "econômico"?

Eu tenho tentado, logico, à medida que meus conhecimentos permitirem.

Comentários

Postagens mais visitadas deste blog

Busca de CEP com o Lazarus - Parte 1 - UrlEncode

Botão Add This para adicionar seu post em qualquer rede

Uso de memória no SQL Server