Existem estudos cientificamente rigorosos sobre os princípios do estilo de codificação? [fechadas]

25

Um princípio de estilo de codificação - por exemplo, o princípio de saída única - é realmente uma coisa boa? Sempre ou apenas algumas vezes? Quanta diferença isso realmente faz?

Quaisquer que sejam suas opiniões, essas são obviamente questões subjetivas. Ou são eles?

Alguém já tentou fazer um estudo objetivo e cientificamente rigoroso dos princípios do estilo de codificação?

Não consigo imaginar como alguém faria um estudo duplo-cego de legibilidade, mas talvez seja possível um duplo ignorante - use alunos que não conhecem o princípio que está sendo estudado como sujeito e não programadores para administrar o estudo.

Steve314
fonte
5
Você pode estar interessado em ler o código completo. Tudo não é mensurável, mas muito é, e você encontrará uma boa visão geral com dados ou fontes brutos neste livro.
Deadalnix 6/10/11
Também é altamente dependente do idioma. Alguns princípios se aplicam a idiomas específicos e não a outros. Por exemplo, o single-exit principlenão se aplica realmente a C ++ causa de RAII
Martin Iorque
@Loki - Eu tive que pensar sobre isso, e não tenho certeza se concordo. É verdade que a RAII foi projetada em grande parte para lidar com exceções, que são pontos de saída alternativos, mas (pelo menos para algumas pessoas) eles contam como pontos de saída alternativos alternativos - sem contar o princípio de saída única da maneira que break, gotoou returnFaz. A saída única IOW não é absoluta em C ++, mas essa é basicamente a minha visão em C e na maioria das outras linguagens. Mas ainda é relevante em um sentido não estrito.
Steve314
1
@ Steve314, o artigo é pelo menos de longe relevante - ele descreve um design para uma metodologia de tal experimento, o que é bastante importante devido à óbvia falta de evidências experimentais adequadamente registradas nessa área.
SK-logic

Respostas:

11

Estou ecoando o comentário de deadalnix: leia o código completo 2 . O autor (Steve McConnell) discute o estilo de codificação em profundidade e freqüentemente faz referência a artigos e dados.

M. Dudley
fonte
Visão geral fundamental e bem apresentada do desenvolvimento profissional de software, espero que um dia eu encontre uma similar para garantir a qualidade. Capítulos sobre programação defensiva e programação de pseudocódigo foram especialmente úteis para mim. O capítulo sobre práticas de desenvolvimento colaborativo parece ser o mais convincente de tudo que li sobre esses assuntos até agora.
Gnat #
Eu não li este livro, e talvez devesse, mas - com base nos comentários das respostas dos mosquitos - esses documentos mencionados são realmente rigorosos e objetivos cientificamente? Se a resposta for "o máximo que puderem", que compromissos foram necessários? Como sugeri na pergunta, era necessário substituir o duplo-cego por algum padrão mais fraco?
precisa saber é o seguinte
@ Steve314: Não sei, não verifiquei as fontes! Mas você nem sempre precisa de rigor científico para estabelecer as melhores práticas. Uma discussão dos prós e contras às vezes é suficiente.
M. Dudley
@emddudley - absolutamente verdade, mas não é exatamente sobre o que essa pergunta era.
precisa saber é o seguinte
@ Steve314: Code Complete seria um ótimo ponto de partida para você, e estou confiante de que algumas de suas referências abordam a questão da análise científica do estilo de codificação.
M. Dudley
12

Duvido muito da possibilidade de um estudo sobre o assunto produzir resultados objetivos e continuarei cético até que me sejam mostradas pesquisas convincentes.

Os programadores que passaram anos lendo e escrevendo códigos que seguiram certo estilo de codificação obviamente o acharão mais legível do que algum estilo de codificação perfeito que eles veriam pela primeira vez em suas vidas.

É exatamente o mesmo com o layout de digitação QWERTY mais comum - é fácil provar que é subótimo em termos de ergonomia (você acha que todos os caracteres da palavra TYPEWRITER foram colocados na linha superior com nossa conveniência diária em mente?) .

Mas alternativas aprimoradas, como Dvorak ou Colemak, nunca pegaram e são improváveis. E, portanto, as pessoas não são mais produtivas com elas - fato. Mesmo que sejam superiores em algum sentido abstrato.

Além disso, seria difícil encontrar indivíduos sem exposição prévia à programação (pois isso contaminaria o resultado do nosso estudo), MAS uma aptidão para a programação E a vontade de participar de um estudo por um período longo o suficiente para mostrar os dois curtos benefícios de longo prazo e benefícios de longo prazo, para que possam ser ponderados um contra o outro ... (não sei se são mutuamente exclusivos, mas os pesquisadores não poderiam simplesmente assumir que nunca são).

Konrad Morawski
fonte
1
Cool, eu nunca tinha ouvido falar de Colemak antes
CaffGeek
1
O @Chad ainda menos conhecido é o Carpal X, com o qual brinquei por um tempo. Achei melhor do que Colemak (cheguei a 90-100 wpm com carpalx). Mesmo que você não pretenda mudar para layouts exóticos, o site carpalx faz uma leitura extremamente interessante sobre a avaliação e otimização de layouts de teclado e a utilização de algoritmos genéticos para essa categoria de problemas. Veja mkweb.bcgsc.ca/carpalx
Konrad Morawski
1
Às vezes, os benefícios marginais de uma abordagem alternativa serão grandes o suficiente para justificar o custo de sua adoção; caso contrário, todos nós ainda estaríamos programando assembler e fortran. Esta resposta realmente não responde à pergunta original sobre se há ou não benefícios marginais. No exemplo de Dvorak, certamente existem e já foram comprovados, mas não são benefícios suficientes para justificar o aprendizado de Dvorak.
Jeremy
@ Jeremy "esta resposta realmente não responde à pergunta original sobre se há ou não benefícios de fato marginais" - o OP não pediu diretamente as conclusões de tais estudos, ele perguntou se alguém havia tentado realizar tais estudos, o que é mais uma pergunta aberta. Respondi apontando algumas razões lógicas sobre por que seria tecnicamente difícil e por que quaisquer resultados desse estudo provavelmente seriam significativamente contaminados por ruído estatístico. Portanto, se minha resposta foi considerada não útil pelas razões que você deu, acho que fui prejudicada injustamente.
Konrad Morawski
1
@ Jeremy, a essência desses custos de adoção é que as pessoas têm um desempenho melhor com uma ferramenta inferior, desde que tenham mais prática. E é exatamente isso que apareceria em qualquer estudo que tentasse examinar até que ponto seus sujeitos lidam com diferentes estilos de codificação. O ruído causado por sua familiaridade / desconhecimento prévio com os estilos de codificação que você gostaria que eles usassem diminuiria o impacto de quaisquer qualidades inatas desses estilos. A menos que você nivele o playground, iniciando por completo. Mas isso apresenta uma dificuldade prática, como apontei no último parágrafo da minha resposta.
Konrad Morawski
4

A resposta é um NÃO definitivo! As `break` e` continue` são más práticas de programação? é um subconjunto desta pergunta, então vou começar com uma resposta pouco modificada para isso ...

Você pode [re] escrever programas sem instruções de interrupção (ou retornos no meio de loops, que fazem a mesma coisa). Mas, ao fazer isso, talvez seja necessário introduzir variáveis ​​adicionais e / ou duplicação de código, as quais normalmente dificultam a compreensão do programa. Pascal (a linguagem de programação do final dos anos 60) era muito ruim, especialmente para programadores iniciantes por esse motivo.

Há um resultado de ciência da computação chamado hierarquia de estruturas de controle de Kosaraju, que remonta a 1973 e é mencionado no (mais) famoso artigo de Knuth Programação estruturada com declarações de 1974. O que S. Rao Kosaraju provou em 1973 é que não é É possível reescrever todos os programas com quebras de profundidade em vários níveis n em programas com profundidade de quebra menor que n sem introduzir variáveis ​​extras. Mas digamos que seja apenas um resultado puramente teórico. (Basta adicionar algumas variáveis ​​extras ?! Certamente você pode fazer isso para se sentir mais conectado com os usuários de mais de 3 mil na stackexchange ...)

O que é muito mais importante do ponto de vista da engenharia de software é um artigo mais recente de 1995, de Eric S. Roberts, intitulado Saídas em Loop e Programação Estruturada: Reabrindo o Debate (doi: 10.1145 / 199688.199815). Roberts resume vários estudos empíricos conduzidos por outros antes dele. Por exemplo, quando um grupo de estudantes do tipo CS101 foi solicitado a escrever código para uma função que implementa uma pesquisa seqüencial em uma matriz, o autor do estudo disse o seguinte sobre os alunos que usaram um intervalo / retorno para sair do seqüencial loop de pesquisa exatamente quando o elemento foi encontrado:

Ainda não encontrei uma pessoa que tentou um programa usando [esse estilo] que produziu uma solução incorreta.

Roberts também diz que:

Os alunos que tentaram resolver o problema sem usar um retorno explícito do loop for se saíram muito menos: apenas sete dos 42 alunos que tentaram essa estratégia conseguiram gerar soluções corretas. Esse número representa uma taxa de sucesso inferior a 20%.

Sim, você pode ser mais experiente do que os alunos do CS101, mas sem usar a instrução break (ou retornar / sair equivalentemente do meio dos loops), eventualmente você escreverá um código que, embora seja bem estruturado nominalmente, seja cabeludo o suficiente em termos de lógica extra variáveis ​​e duplicação de código que alguém, provavelmente você mesmo, colocará erros de lógica ao tentar seguir alguma idéia de estilo de codificação "correto".

E há um problema maior aqui além das declarações do tipo retorno / quebra, portanto, essa pergunta é um pouco mais ampla que a das quebras. Os mecanismos de tratamento de exceções também estão violando o paradigma do ponto de saída única, de acordo com alguns

Portanto, basicamente qualquer pessoa que argumentou acima que o princípio de saída única ainda é útil hoje também está argumentando contra o paradigma de manipulação de exceções, a menos que seja usado da maneira extremamente constritiva descrita no último link; essas diretrizes basicamente restringem todas as exceções de uma função para throw (), ou seja, nenhuma propagação de exceções entre funções é permitida. Aproveite o seu novo Pascal com sintaxe semelhante ao C ++.

Vejo de onde veio a noção de "apenas um retorno"?que a opinião predominante neste site é o contrário do que publiquei aqui, então entendo perfeitamente por que já recebi votos negativos, mesmo sendo a primeira resposta aqui a realmente fornecer algo que a pergunta foi feita: algumas informações sobre testes de usabilidade reais focadas no problema de saída única. Acho que não devo deixar o conhecimento atrapalhar preconceitos, especialmente em um site de gamificação. Vou continuar editando a Wikipedia a partir de agora. Pelo menos as informações de boas fontes são apreciadas e reivindicações vagas ou incorretas que pretendem ser apoiadas por fontes acabam sendo proibidas. Neste site, acontece o contrário: opiniões não fundamentadas por fatos dominam. Eu espero que um mod apague esta última parte, mas pelo menos esse cara saberá por que você me perdeu para sempre como colaborador aqui.

Fizz
fonte
Não diminuí a votação, mas no seu "Mas ao fazer isso, talvez seja necessário introduzir variáveis ​​adicionais e / ou duplicação de código, as quais normalmente dificultam a compreensão do programa". ponto, isso é uma afirmação subjetiva. Concordo que adicionar uma duplicação de variável ou código dificulta a compreensão, mas sem dúvida adicionar um goto também dificulta a compreensão, além disso, sem dúvida, o dano causado pela duplicação pode ser atenuado fatorando o código duplicado em uma função (embora a IMO esteja movendo complexidade no gráfico de chamadas não a elimina automaticamente).
Steve314
Eu vi seu ponto de vista sobre o artigo de 1995 somente depois desse último comentário e decidi votar novamente - ponto interessante. Acho que seu voto negativo pode ser mais porque sua postagem é longa e começa com um ponto subjetivo; portanto, provavelmente o voto negativo não leu a coisa toda (o mesmo que eu, a princípio). Basicamente, é uma boa ideia apresentar seu ponto real mais cedo.
Steve314
Enfim, acho que um monte de gente pensar em exceções como tipo de alternativas pontos de saída alternativas - porque eles foram feitos para casos de erro (tipo de) que realmente não contam. Eu entendo que isso é um pouco sensível à cultura da linguagem. Em algumas línguas, "exceção" é mais do que o nome - um caso de sucesso excepcional é válido (e o IIRC Stroustrup disse algo assim sobre C ++, levantando um ponto filosófico sobre se um erro é um erro se for tratado). Alguns até dizem que as exceções são apenas outro fluxo de controle a ser usado sempre que fornece o fluxo de controle necessário.
Steve314
1
@ Steve314 " mais, sem dúvida, o dano causado pela duplicação pode ser atenuado fatorando o código duplicado em uma função " Colocando fora da linha e fora da vista imediata parte de uma lógica da função, uma parte que não faz sentido isolado. Tornando ainda mais difícil entender a lógica da função.
precisa
1
@curiousguy - sim, isso é verdade, e provavelmente parte da intenção do meu ponto de "mover a complexidade para o gráfico de chamadas". Minha religião é que toda escolha que você faz é uma troca; portanto, esteja ciente de todas as opções plausíveis e de suas vantagens e desvantagens, e conhecer as mitigações comuns é importante, mas tome cuidado caso a cura seja pior que a doença. Exceto, é claro, que parte do trade-off é quanto tempo você gasta (ou perde) se preocupando com as coisas.
Steve314
1

http://dl.acm.org/citation.cfm?id=1241526

http://www.springerlink.com/content/n82qpt83n8735l7t/

http://ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=661092

[Suas perguntas parecem ter sido respondidas por uma única palavra, "sim". Disseram-me, no entanto, que fornecer respostas curtas é "desdenhoso" da questão. Se você acha que fui desdém, sinalize a resposta para que um moderador possa excluí-la.]

S.Lott
fonte
1
@ luis.espinal: Para que fim? Quais informações o texto conteria? A pergunta divaga um pouco. Que parte da pergunta deve ser abordada com algum texto?
S.Lott
1
Por uma questão de estilo, e talvez para fornecer mais informações que os resumos dos links podem fornecer (considerando que não sabemos se o OP é um membro pagante da ACM / IEEE / Springer Verlag com acesso aos artigos completos e encontra respostas para suas perguntas.) Por exemplo, o resumo do artigo da ACM não menciona o estilo de codificação. No máximo, ele fala sobre corroborar o teorema do programa estruturado (que por si só não fala sobre o problema de retorno único ou múltiplo). Então você poderia ter explicado por que esse link é relevante.
Luis.espinal 06/10/11
1
O terceiro artigo (felizmente eu tenho acesso ao IEEE Xplore) não parece relacionado ao que o OP está pedindo, até onde eu sei. É um artigo maravilhoso para você, que estou imprimindo para uma leitura mais dedicada posteriormente. Então, talvez você também possa ter explicado como este artigo ajuda o OP a responder sua pergunta. No geral, parece que você simplesmente juntou vários links. Não é uma maneira de ser desdenhoso (a menos que tenha sido sua intenção), mas, novamente, não vejo como isso ajudou o OP. E é por isso que um pôster deve adicionar algum texto ao longo de seus links. Então agora você sabe por que eu disse isso;)
luis.espinal
1
da boca do OP Is a coding style principle - e.g. the single-exit principle - really a good thing?- isso contextualiza a pergunta que ele está fazendo, sobre estilos de codificação. Além disso, o estilo de codificação não é o mesmo que a metodologia de programação, em particular os métodos de design de alto nível, que são o foco do artigo IEEE (claramente indicado pelos autores.) É por isso que digo "não" - os escopos são completamente diferentes.
Luis.espinal 06/10/11
1
Eu suspeito de onde o OP está vindo. Ele está claramente declarando estilos de codificação (não metodologias) e, em particular, retornos únicos versus múltiplos. Eu tive que lidar com isso algumas vezes com código bem escrito e inerentemente auto-evidente, usando várias instruções de retorno sendo reescritas em versões mais complicadas usando retornos únicos (em particular em grandes organizações grandes em burocracia) * como por "o processo". E se pergunta (e desafia com evidências) a validade, usabilidade e relação custo-benefício de tais mandatos arbitrários. As pessoas que forçam tais mandatos ainda vivo na década de 60: /
luis.espinal
1

É um princípio de estilo de codificação - por exemplo, o princípio de saída única

As pessoas que ainda se preocupam com uma saída única ou múltipla ainda estão presas no final dos anos 60. Naquela época, essa discussão era importante, pois estávamos na infância de programadores estruturados, e havia um campo bastante numeroso proclamando que as descobertas por trás do Teorema do Programa Estruturado da Bohm-Jacopini não eram universalmente aplicáveis ​​a todas as construções de programação.

É algo que deveria ter sido resolvido há muito tempo. Bem, foi acertado (quase 4 décadas para ser preciso, tanto na Academia quanto na indústria), mas as pessoas (aquelas que são absolutamente a favor ou contra) não estão prestando atenção.

Quanto ao restante das minhas respostas, é tudo relativo (o que não está no software?):

  • realmente uma coisa boa?

Sim. Na maioria das vezes, para o caso geral, com advertências específicas para casos extremos e construções de programação específicas da linguagem.

Sempre ou apenas algumas vezes?

A maior parte do tempo.

Quanta diferença isso realmente faz?

Depende.

Código legível vs código ilegível. Maior complexidade (que agora deveríamos saber aumenta a probabilidade de introdução de erros) versus complexidade mais simples (e, portanto, menor probabilidade de erros). Linguagens cujos compiladores não adicionam um retorno implícito (por exemplo, Pascal, Java ou C #) e aqueles que padrão para int (C e C ++).

No final, é uma habilidade aprimorada com homem / hora atrás de um teclado. Às vezes, não há problema em ter várias instruções de retorno, como aqui (em algum pseudocódigo de Pascal'esque):

function foo() : someType
  begin
  if( test1 == true )
  then
    return x;
  end
  doSomethignElseThatShouldnHappenIfTest1IsTrue();
  return somethingElse();
end;

A intenção é clara e o algoritmo é pequeno o suficiente e descomplicado o suficiente para não garantir a criação de uma variável 'flag' que contém o valor de retorno eventual usado em um único ponto de retorno. O algoritmo pode estar com erro, mas sua estrutura é simples o suficiente para que o esforço em detectar um erro seja (muito provavelmente) insignificante.

Às vezes não é (aqui usando um pseudocódigo do tipo C):

switch(someVal)
{
case v1 : return x1;
case v2 : return x2:
case v3 : doSomething(); // fall-through
case v4: // fall-through
case v5: // fall-through
case v6: return someXthingie;
...
...
default:
   doSomething(); // no return statement yet
}

Aqui, o algoritmo não possui uma estrutura simples, e a instrução switch (do tipo C) permite etapas de transição que podem ou não ser feitas intencionalmente como parte do algoritmo.

Talvez o algoritmo esteja correto, mas mal escrito.

Ou talvez, por forças externas além da capacidade do programador, essa seja a representação real (e correta) de um algoritmo legitimamente necessário.

Talvez esteja errado.

Descobrir a verdade de tudo isso requer muito mais esforço do que no exemplo anterior. E aqui está algo em que eu acredito fortemente (lembre-se de que não tenho estudos formais para apoiar isso):

Supondo que um trecho de código que seja considerado correto:

  1. Várias instruções de retorno aumentam a legibilidade e a simplicidade de um trecho de código, se o trecho representar um algoritmo simples com uma estrutura de fluxo inerentemente simples. Por simples, não quero dizer pequeno, mas quero dizer intrinsecamente compreensível ou auto-evidência , o que não requer esforço de leitura desproporcional (nem induz as pessoas a vomitar, amaldiçoar a mãe de alguém ou engolir uma bala quando precisam lê-la. )

  2. Uma única declaração de retorno aumenta a legibilidade e a simplicidade de um pedaço de código se o valor de retorno for calculado durante a execução do algoritmo ou se as etapas no algoritmo responsável por calculá-lo puderem ser agrupadas em um local dentro da estrutura do algoritmo. .

  3. Uma única declaração de retorno diminui a legibilidade e a simplicidade de um pedaço de código se exigir atribuições a uma ou mais variáveis ​​de flag, com os locais de tais atribuições não sendo uniformemente localizados em todo o algoritmo.

  4. Várias instruções de retorno diminuem a legibilidade e a simplicidade de um pedaço de código, se as instruções de retorno não são distribuídas uniformemente pelo algoritmo e se demarcam blocos de código mutuamente exclusivos que não são uniformes em tamanho ou estrutura entre si.

Isso está intimamente relacionado à complexidade de um trecho de código em questão. E isso, por sua vez, está relacionado a medidas de complexidade ciclomática e de halstead. A partir disso, pode-se observar o seguinte:

Quanto maior o tamanho de uma sub-rotina ou função, maior e mais complexa é a estrutura do fluxo de controle interno e maior a probabilidade de você enfrentar uma questão de usar declarações de retorno múltiplas ou únicas.

A conclusão disso é: mantenha suas funções pequenas fazendo uma coisa e apenas uma coisa (e fazendo bem). Se exibirem métricas de complexidade ciclomática e de halstead nominalmente pequenas, elas não apenas serão provavelmente corretas e implementarão tarefas que são compreensíveis, como também suas estruturas internas serão relativamente evidentes.

Então, e só então você pode facilmente e sem perder muito sono, pode decidir se deve usar um único retorno e vários retornos sem correr muitos riscos de introduzir erros com qualquer uma das opções.

Pode-se também analisar tudo isso e sugerir que, quando as pessoas lutam com a questão de retornos únicos ou múltiplos retornos, é porque - por inexperiência, estupidez ou falta de ética no trabalho - eles não escrevem código limpo e tendem a escrever funções monstruosas com total desconsideração das medidas ciclomáticas e halstead.

luis.espinal
fonte
1
O tipo de retorno C ++ não é padrão para int: não há um tipo de retorno padrão, portanto ele deve ser especificado em todos os casos.
Sjoerd
Antes de escrever essa pergunta - programmers.stackexchange.com/questions/58237/… . Basicamente, estou defendendo a conscientização do princípio, mas não o seguindo estritamente - se todos os pontos de saída forem óbvios, fico feliz. O que quero dizer aqui - só porque mencionei um princípio como exemplo, não significa que estou defendendo esse princípio, e certamente não em sua forma estrita. Minha opinião subjetiva é apenas isso, porém - talvez exista um argumento mais forte para minha opinião, ou talvez exista um argumento forte de que estou errado.
Steve314
O que é "padrão para int"?
precisa
Quero dizer, e eu deveria ter qualificado, que a maioria dos compiladores simplesmente "empurra" o valor de um registrador de acumulador como um valor de retorno se o código tiver uma ramificação de execução sem um valor de retorno explícito. Isso, na verdade, significa retornar o resultado da última operação aritmética (qualquer que seja o lixo que possa ser) na forma int. E isso certamente seria lixo (e, portanto, comportamento indefinido), independentemente do que a função pretendia fazer em primeiro lugar. C e C ++ podem avisá-lo, mas as compilações permitem compilar, a menos que você use -Werror ou algo semelhante.
Luis.espinal