Acabei de escrever uma função que abrange aproximadamente 100 linhas. Ao ouvir isso, você provavelmente está tentado a me falar sobre responsabilidades únicas e me incentiva a refatorar. Esse também é meu instinto, mas aqui está o problema: a função faz uma coisa. Ele executa uma manipulação complexa de strings, e o corpo da função consiste principalmente em um regex detalhado, dividido em muitas linhas documentadas. Se eu dividir o regex em várias funções, sinto que realmente perderia a legibilidade, pois estou trocando de idioma efetivamente e não poderei tirar proveito de alguns recursos oferecidos pelo regex . Aqui está agora a minha pergunta:
Quando se trata de manipulação de strings com expressões regulares, os grandes corpos funcionais ainda são um antipadrão? Parece que os grupos de captura nomeados têm um propósito muito semelhante às funções. A propósito, eu tenho testes para cada fluxo no regex.
fonte
Respostas:
O que você encontra é a dissonância cognitiva resultante de ouvir pessoas que favorecem a adesão servil às diretrizes, sob o pretexto de "melhores práticas", em detrimento da tomada de decisão fundamentada.
Você claramente fez sua lição de casa:
Se algum desses pontos não fosse verdade, eu seria o primeiro da fila a dizer que sua função precisa funcionar. Portanto, há um voto para deixar o código como está.
A segunda votação vem do exame de suas opções e o que você obtém (e perde) de cada uma:
Essa decisão se resume a que você valoriza mais: legibilidade ou duração. Eu caio no campo que acredita que a duração é boa, mas a legibilidade é importante e levará o último ao longo do primeiro em qualquer dia da semana.
Conclusão: se não estiver quebrado, não conserte.
fonte
Honestamente, sua função pode "fazer uma coisa", mas, como você mesmo declarou
o que significa que o seu código regular faz muitas coisas. E eu acho que poderia ser dividido em unidades menores, individualmente testáveis. No entanto, se essa é uma boa ideia, não é fácil responder (especialmente sem ver o código real). E a resposta correta pode ser "sim" ou "não", mas "ainda não, mas da próxima vez você precisará alterar algo nesse registro".
E este é o ponto principal - você tem um pedaço de código escrito na linguagem normal . Essa linguagem não fornece bons meios de abstração em si (e não considero "grupos de captura nomeados" como um substituto para funções). Portanto, a refatoração "no idioma normal" não é realmente possível, e a junção de expressões regulares menores com o idioma do host pode não melhorar a legibilidade (pelo menos, você sente isso, mas tem dúvidas, caso contrário não teria postado a pergunta) . Então aqui está o meu conselho
mostre seu código para outro desenvolvedor avançado (talvez em /codereview// ) para garantir que outras pessoas pensem na legibilidade da maneira que você faz. Esteja aberto à idéia de que outras pessoas podem não encontrar um registro de 100 linhas tão legível quanto você. Às vezes, a noção de "não é facilmente quebrável em pedaços menores" pode ser superada apenas por um segundo par de olhos.
observe a real capacidade de evolução - a sua brilhante experiência de registro ainda parece tão boa quando novos requisitos chegam e você precisa implementá-los e testá-los? Enquanto o seu reg exp funcionar, eu não o tocaria, mas sempre que algo precisa ser mudado, reconsideraria se era realmente uma boa ideia colocar tudo nesse grande bloco - e (sério!) Repensar se dividir em pedaços menores não seriam uma opção melhor.
observe a capacidade de manutenção - você pode depurar efetivamente o reg exp na forma atual muito bem? Especialmente depois que você precisa alterar alguma coisa e agora seus testes dizem que algo está errado, você tem um depurador reg exp ajudando você a encontrar a causa raiz? Se a depuração for difícil, isso também seria uma ocasião para reconsiderar seu design.
fonte
Às vezes, uma função mais longa que faz uma coisa é a maneira mais apropriada de lidar com uma unidade de trabalho. Você pode facilmente assumir funções muito longas quando começa a lidar com a consulta de um banco de dados (usando sua linguagem de consulta favorita). Tornar uma função (ou método) mais legível e limitá-la ao seu objetivo declarado é o que eu consideraria o resultado mais desejável de uma função.
O comprimento é um "padrão" arbitrário quando se trata do tamanho do código. Onde uma função de 100 linhas em C # pode ser considerada longa, seria pequena em algumas versões de montagem. Vi algumas consultas SQL que estavam dentro do intervalo de 200 linhas de código que retornaram um conjunto muito complicado de dados para um relatório.
Código totalmente funcional , que é tão simples como você pode razoavelmente fazê-lo é o objetivo.
Não mude apenas porque é longo.
fonte
Você sempre pode dividir a regex em sub-regexes e gradualmente compor a expressão final. Isso pode ajudar na compreensão de um padrão muito grande, principalmente se o mesmo sub-padrão for repetido várias vezes. Por exemplo em Perl;
fonte
Eu diria que quebra se for quebrável. do ponto de vista da manutenibilidade e talvez da resuabilidade, faz sentido quebrá-la, mas é claro que você deve considerar a natureza de sua função e como obter informações e o que ela retornará.
Lembro-me de que estava trabalhando na análise de dados fragmentados de fluxo em objetos, então o que fiz basicamente foi dividi-los em duas partes principais, uma foi a construção de uma unidade completa de String com texto codificado e na segunda parte a análise dessas unidades no dicionário de dados e organização eles (pode ser uma propriedade aleatória para um objeto diferente) e depois atualizar ou criar objetos.
Também pude dividir cada parte principal em várias funções menores e mais específicas, de modo que no final eu tinha 5 funções diferentes para fazer a coisa toda e pude reutilizar algumas das funções em lugares diferentes.
fonte
Uma coisa que você pode ou não considerar é escrever um pequeno analisador no idioma que você está usando, em vez de usar um regex nesse idioma. Pode ser mais fácil ler, testar e manter.
fonte
Regexes gigantes são uma má escolha na maioria dos casos. Na minha experiência, eles são freqüentemente usados porque o desenvolvedor não está familiarizado com a análise (consulte a resposta de Thomas Eding ).
De qualquer forma, vamos supor que você queira manter uma solução baseada em regex.
Como não conheço o código real, examinarei os dois cenários possíveis:
O regex é simples (muitas correspondências literais e poucas alternativas)
Nesse caso, os recursos avançados oferecidos por um único regex não são indispensáveis. Isso significa que você provavelmente se beneficiará da divisão.
O regex é complexo (muitas alternativas)
Nesse caso, você não pode realisticamente ter uma cobertura completa de teste, porque provavelmente possui milhões de fluxos possíveis. Então, para testá-lo, você precisa dividi-lo.
Posso não ter imaginação, mas não consigo pensar em nenhuma situação do mundo real em que um regex de 100 linhas seja uma boa solução.
fonte