Como posso definir e medir a simplicidade no código?

13

Há muitas respostas na minha pergunta anterior sobre simplicidade relacionada à legibilidade que me ajudaram a ver que minha definição e compreensão da simplicidade no código eram, possivelmente, incorretas.

Como posso definir simplicidade no código? Quais medidas e métricas de software estão disponíveis para medir a simplicidade do código?

Richard
fonte
2
@ MarkTrapp Existem outras maneiras de discutir a simplicidade do código sem tópicos da engenharia de software empírica, tópicos com os quais estou muito menos familiarizado. Por exemplo, discutindo a simplicidade em termos da capacidade de escrever testes automatizados. Minhas habilidades e conhecimentos me permitem responder a essa pergunta da perspectiva de um engenheiro de software emprical, enquanto outros podem responder de perspectivas alternativas. A adição dessa declaração à pergunta limita significativamente o número de respostas úteis, tornando-a (IMO) muito localizada. Se você quiser adicioná-lo, pode, mas essa é uma boa pergunta.
Thomas Owens
2
@ThomasOwens As perguntas reais têm respostas , não idéias ou opiniões. Reduzir o escopo para que todos interpretem como responder à pergunta da mesma maneira é exatamente o que é o Stack Exchange. Pode haver mais de uma abordagem para resolver o problema, mas há apenas um problema declarado sem ambiguidade.
Em seu estado atual, existem muito poucas respostas para essa pergunta (minha resposta aborda o ponto de vista empírico da engenharia de software, com as métricas comuns - provavelmente existem outras). Não faz sentido excluir respostas que fornecem alternativas válidas de outras perspectivas, que é o que a redação desta pergunta faz. Não concordo totalmente com essas edições e a pergunta deve ser revertida para sua forma original.
Thomas Owens
@ MarkTrapp O problema é inequívoco: como determino a simplicidade do código? Existem várias boas respostas. O meu é usar técnicas empíricas de engenharia de software para medir a complexidade. Outro pode ser escrever testes automatizados e, se for difícil escrever bons testes, o código é complexo - uma resposta perfeitamente válida. Pode haver outros que eu não estou ciente. Se você precisar medir a complexidade / simplicidade de uma base de código, a pergunta deve ser redigida de forma a permitir que todas as alternativas sejam apresentadas para que o solicitante possa escolher a melhor solução para seu caso específico.
Thomas Owens

Respostas:

16

As métricas mais comuns para medir a complexidade (ou simplicidade, se você considerar a simplicidade o oposto da complexidade) são a Complexidade Ciclomática de McCabe e a Métrica da Complexidade de Halstead .

A complexidade ciclomática mede o número de caminhos distintos através de uma determinada unidade, geralmente um método ou função, embora também possa ser calculado em uma classe. À medida que o número de caminhos aumenta, fica mais difícil lembrar o fluxo de dados através de um determinado módulo, que está relacionado ao conceito de memória de trabalho . A alta complexidade ciclomática tende a indicar dificuldade na capacidade de testar um módulo - são necessários mais casos de teste para cobrir os vários caminhos do sistema. Também houve estudos que associaram alta complexidade ciclomática a altas taxas de defeitos. Normalmente, uma complexidade ciclomática de 10 indica que uma unidade deve ser revisada e possivelmente refatorada.

As medidas de complexidade de Halstead usam as entradas de operadores e operandos totais e distintos para calcular o volume, a dificuldade e o esforço de um pedaço de código. A dificuldade, que é o (número de operadores únicos / 2) * (número total de operandos / número de operandos únicos), está ligada à capacidade de ler e entender o código para tarefas como aprender o sistema ou executar uma revisão de código. Novamente, você pode contar isso no nível do sistema, no nível da classe ou no nível do método / função. Existem algumas publicações sobre o cálculo dessas medidas aqui e aqui .

Simplesmente contar linhas de código também pode lhe dar uma idéia da complexidade. Mais linhas de código significa que há mais para ler e entender em um módulo. Eu hesitaria em usar isso como uma medida independente. Em vez disso, eu o usaria com outras medidas, como o número de defeitos em um determinado módulo para obter a densidade do defeito. Uma alta densidade de defeitos pode indicar problemas ao escrever testes e executar revisões de código, que podem ou não ser causadas por códigos complexos.

Fan-in e fan-out são outras duas métricas, relacionadas ao fluxo de dados. Conforme definido aqui , fan in é a soma dos procedimentos chamados, parâmetros lidos e variáveis ​​globais lidas e fan out é a soma dos procedimentos que chamam um determinado procedimento, parâmetros gravados (expostos a usuários externos, passados ​​por referência), e variáveis ​​globais gravadas em. Novamente, a alta entrada e saída de ventiladores pode ser um indicativo de um módulo que pode ser difícil de entender.

Em paradigmas específicos, pode haver outras medidas ou métricas também úteis. Por exemplo, no mundo orientado a objetos, o monitoramento do acoplamento (desejo baixo), coesão (desejo alto) e profundidade da herança (desejo baixo) pode ser usado para avaliar o quão simples ou complicado é um sistema.

Obviamente, é importante perceber que muitas medidas e métricas são simplesmente indicadores. Você precisa usar seu julgamento para determinar se é necessário refatorar para aumentar a simplicidade ou se não vale a pena o esforço para fazê-lo. Você pode fazer as medições, calcular as métricas e aprender sobre seu código, mas não deseja projetar seu sistema pelos números. Por fim, faça o que faz sentido.

Thomas Owens
fonte
5
Eu sei que você mencionou, mas é importante enfatizar que a complexidade ciclomática é realmente útil apenas no nível da função / método e se torna significativamente mais subjetiva / inútil em níveis mais altos.
Ryathal 6/12/11
O problema é que, embora essas medidas sejam boas, um guia geral. Existem vários casos em que programas "ruins" são bem avaliados, por exemplo, com uma dúzia de funções, onde uma única função com mais dois parâmetros seria suficiente e, inversamente, muitos programas "bons" que são soluções bem escritas para um problema complexo podem ser bem-sucedidos. seriamente.
James Anderson
@ James Eu expliquei isso explicitamente. Qualquer medida ou métrica precisa ser tomada no contexto como um indicador de que algo deve ser analisado. É necessário o julgamento de um engenheiro para determinar se é necessária uma ação corretiva e qual é essa ação. No entanto, a menos que você esteja coletando dados ativamente, não há uma maneira empírica de saber sobre possíveis problemas.
Thomas Owens
7

Em vez de olhar para um modo formal de definir simplicidade, eu gostaria de definir a simplicidade como um atributo da qualidade da escrita de código.

Não estou colocando um pouco de simplicidade, mas quando você chama algo simples ou não.

1. Code Traversal:
como é fácil navegar pelo código? É fácil identificar onde as funções da API estão gravadas? É fácil entender os fluxos de chamadas, por exemplo, quais métodos estão chamando outras pessoas (e por que) - existem boas máquinas de estado implementadas ou algoritmos identificados de maneira limpa?

Quando a passagem do código é fácil, é fácil seguir o código .

2. Nomeação
Embora outros padrões de codificação ajudem a tornar o código mais limpo - o mais importante é a nomeação de classes / instâncias de objetos / Variáveis ​​/ métodos. O uso de nomes claros e inequívocos tem claramente um grande impacto na simplicidade do código. Quando é difícil identificar um nome simples, é um sinal de que você pode repensar a ideia de ser essa variável / método.

3. Interpretação e referências
Cada um dos seus métodos tem um papel claro a desempenhar. Cada variável / atributo é fácil de determinar o papel que está desempenhando? Quando um pedaço de código faz algo que implica suposições ou afeta um conjunto de variáveis ​​não relacionadas, pode se tornar um pesadelo de manutenção.

4. Dependência ou acoplamento
É difícil julgar apenas olhando o código, mas fica muito evidente se alguém tentar corrigir seus erros. Quando algumas outras coisas mudam em outro objeto, a operação aqui muda? Essas mudanças são óbvias? Você precisa alterar a API com tanta frequência para acomodar coisas. Isso sugere que as relações entre módulos não são simples

5. Insere usuário ou aplicativos
Finalmente, quão simples são as entradas de usuário ou aplicativo são aceitas na API / UI? Quando vários usuários / aplicativos possíveis (para diferentes propósitos) precisam fornecer a você - eles são óbvios? Existem estados / detalhes que não estão relacionados à abstração mais alta, mas ainda vão e voltam à interface?

Uma pergunta simples que eu geralmente faria é a seguinte: Se em vez de um programa, se eu pedisse a mesma função a ser desempenhada por um humano, eu teria preenchido essas informações em um formulário em papel ? Caso contrário, não sou simples o suficiente aqui.

Não direi que esta lista é completa, mas acho que critérios são o quão fácil ou difícil é usar e modificar o software. Isso é simples.

Dipan Mehta
fonte
1
Ele pediu medições e métricas. Estes são subjetivos, então eu não acho que eles são muito importantes.
psr
@ psr eu concordo com você. Também ficou muito evidente na resposta da resposta de Thomas. No entanto, ele mencionou a simplicidade relacionada à legibilidade . A resposta de Thomas lida com a complexidade ciclomática - isso mostra o quão complexo é testar o código e não o quão complexo é o código em termos de legibilidade e pode estender a manutenção . Estes são dois conceitos muito diferentes. Foi precisamente por isso que escrevi esta resposta para colocar a forte contradição. Infelizmente, para meu conhecimento, não há métricas que se refiram à simplicidade do código em termos de legibilidade.
Dipan Mehta 6/12/11
"use nomes que são impossíveis de serem mal interpretados" - IMHO, esse objetivo é muito alto, para um objetivo irrealista e impossível. Prefiro não tentar ser tão definido e simplesmente dizer "use nomes claros e inequívocos".
Péter Török
@ PéterTörök eu concordo. Eu acho que geralmente, em muitas organizações, regras muito claramente definidas para convenções de nomenclatura e ainda persiste alguma confusão sobre a intenção dessa variável específica. Portanto, a ênfase era dizer que a clareza de propósito equivale à simplicidade em oposição a uma regra formal. Pode ser que eu tenha exagerado na forma como descrevi. Obrigado.
Dipan Mehta
A complexidade ciclomática do @Dipan está relacionada à legibilidade do código, através da memória de trabalho. Código com alta complexidade ciclomática (ou mesmo com alta profundidade de bloco) é difícil de manter na memória de trabalho e, portanto, mais difícil de ler diretamente.
Thomas Owens
0

Não conheço nenhuma boa métrica existente para simplicidade de código (isso não significa que elas não existem - apenas que eu não as conheço). Eu poderia propor alguns, talvez alguns ajudem:

  • Simplicidade dos recursos de idioma utilizados: se o idioma tiver recursos que possam ser considerados "avançados" e "simples", você poderá contar o número de ocorrências dos recursos avançados. Como você define "avançado" pode ser um pouco mais subjetivo. Suponho que alguns possam dizer que isso também é como medir a "inteligência" de um programa. Um exemplo comum: alguns podem dizer que o ?:operador deve ser um recurso "avançado", outros podem discordar. Não sei como seria fácil escrever uma ferramenta que possa testar isso.

  • Simplicidade de construções no programa: Você pode medir o número de parâmetros que uma função aceitará. Se você tiver> n % de todas as funções com parâmetros > m , poderá optar por considerá-lo como não simples, dependendo de como definir n e m (talvez n = 3 e m = 6?). Acho que existem algumas ferramentas de análise estática que podem medir isso - acho que o JTest simplesmente mediu funções com parâmetros > m .

  • Você pode tentar contar o número de loops aninhados ou estruturas de controle. Na verdade, acho que isso não é uma métrica ruim e acho que existe um nome para ela (não me lembro bem). Mais uma vez, acho que existem ferramentas (mais uma vez, como o JTest) que podem medir isso, até certo ponto.

  • Você pode tentar medir a "refatorabilidade". Se o seu código contiver muitos trechos de código que poderiam ser refatorados, mas não são , talvez isso não fosse simples. Também me lembro, desde o tempo em que trabalhei com o JTest, que ele também tentava medir isso, mas lembro que não concordei com isso nesse caso, então YMMV.

  • Você pode tentar medir o número de camadas entre diferentes partes do seu sistema. Por exemplo: quantas partes de código diferentes tocam nos dados provenientes de um formulário da Web antes de serem armazenados no banco de dados? Pode ser complicado medir adequadamente ...

FrustratedWithFormsDesigner
fonte
2
Eu acredito que o número 3 é chamado profundidade do bloco. Também está relacionado à complexidade ciclomática se houver estruturas de controle decisórias envolvidas.
Thomas Owens
Nenhuma explicação de voto negativo?
FrustratedWithFormsDesigner
Não posso concordar com a "simplicidade dos recursos de idioma". Recursos avançados existem para simplificar o código. Usar apenas os recursos básicos e simples irá obscurecer o que o código está realmente fazendo, inevitavelmente leva ao vazamento de camadas de abstração. Recursos avançados de linguagem permitem expressar níveis mais altos de abstração, deixando seu código muito mais denso e legível. Quanto mais recursos avançados você estiver usando (sabiamente, é claro), melhor para a simplicidade. Basta comparar um código no, digamos, Matlab (que é "avançado" de fato) com um código Fortran semelhante feito apenas de recursos básicos.
SK-logic
E também não vou concordar com várias métricas de camadas. Se você pode fazer algo em uma dúzia de etapas triviais e limpas ou em uma transformação de uma torção, é melhor fazê-lo em várias etapas. Muitas camadas simples e claramente separadas são muito melhores (e mais simples) do que uma única camada torcida.
SK-logic
@ SK-logic: Eu acho que deveria ter chamado essa "esperteza", é mais perto do que eu quis dizer. Eu diria apenas que coisas como esse ?:são um problema quando estão aninhadas a 5 profundidade. Quanto às camadas, as camadas separadas de maneira limpa são melhores que uma camada complicada. Mas 7 camadas principalmente redundantes, quando apenas 2 ou 3 eram necessárias, é uma coisa ruim.
FrustratedWithFormsDesigner