É esse uso de um exagero constante simbólico?

34

Sou bastante novo em engenharia de software e, como exercício de aprendizado, escrevi um jogo de xadrez. Meu amigo deu uma olhada e apontou que meu código parecia

for (int i = 0; i < 8; i++){
    for (int j = 0; j < 8; j++){

enquanto ele insistia que deveria ser

for (int i = 0; i < CHESS_CONST; i++){
    for (int j = 0; j < CHESS_CONST; j++){

com um nome de símbolo melhor no qual não posso me preocupar em pensar agora.

Agora, é claro, eu sei geralmente evitar usar números mágicos, mas sinto que desde

  1. esse número nunca mudará;
  2. o nome não poderia ser tão descritivo de qualquer maneira, já que o número é usado em muitos lugares ao longo do código; e
  3. qualquer pessoa que passe pelo código-fonte de um programa de xadrez deve saber o suficiente sobre xadrez para saber para que serve o 8,

realmente não há necessidade de uma constante simbólica.

Então, o que vocês acham? Isso é um exagero, ou devo apenas ir à convenção e usar um símbolo?

chocolatedevil
fonte
46
CHESS_CONST é muito pior do que usar o número 8, mas uma constante com um nome descritivo seria uma melhoria. Você diz que alguém deve saber o que 8 significa no código, mas isso não é verdade. Um literal inteiro sem contexto pode significar várias coisas, como um número de movimentos, número de peças no quadro e assim por diante. Um nome descritivo para a constante tornaria a intenção clara e, portanto, o código mais fácil de entender.
precisa saber é o seguinte
43
Pessoalmente, o que eu acho muito mais chocante do que o número mágico é os nomes ie jpara as variáveis de loop. Durante toda a minha vida, não consigo descobrir qual deve representar a classificação e qual deve representar o arquivo. As classificações variam de 1..8 e os arquivos variam de a..h, mas no seu caso, ambos ie jde 0..7, de modo que não me ajuda a ver qual é qual. Existe alguma crise internacional de falta de cartas que eu não conheça, ou o que há de errado em renomeá-las ranke file?
Jörg W Mittag
4
Jogue como advogado do diabo por um segundo; Imagine ler o código de xadrez de alguém em que eles usaram um número mágico 8. Você pode supor que sabe para que serve? Como você pode ter 100% de certeza? Existe alguma possibilidade de que isso possa significar algo mais? Não seria apenas um pouco melhor se você nem precisasse fazer uma suposição? Quanto tempo você gasta rastreando o código para descobrir se sua suposição está correta? Você gastaria menos tempo se o código tivesse se auto-documentado usando um nome significativo e perspicaz?
Ben Cottrell
16
@ JacquesB: De fato. Há 8 fileiras, 8 arquivos, 8 peões. Alguns mecanismos de xadrez usam um sistema de pontos em que certas peças, certos campos e certos movimentos têm pontos anexados a eles, e o mecanismo escolhe o movimento com a maior pontuação. 8 pode ser uma pontuação. 8 pode ser uma profundidade de pesquisa padrão. Pode ser praticamente qualquer coisa.
Jörg W Mittag
9
Eu consideraria usar um loop foreach, por exemplo foreach(var rank in Ranks). Também consideraria mesclar os dois loops em um onde cada elemento é uma tupla (classificação, arquivo).
código é o seguinte

Respostas:

101

IMHO seu amigo está certo ao usar um nome simbólico, embora eu ache que o nome deve ser definitivamente mais descritivo (como em BOARD_WIDTHvez de CHESS_CONST).

Mesmo quando o número nunca muda durante a vida útil do programa, pode haver outros lugares no programa em que o número 8 ocorrerá com um significado diferente. Substituir "8" por BOARD_WIDTHqualquer que seja a largura da placa e usar outro nome simbólico quando outra coisa significa torna esses significados explícitos, óbvios e seu programa geral mais legível e fácil de manter. Ele também permite que você faça uma pesquisa global em seu programa (ou uma pesquisa de símbolo reverso, se o ambiente o fornecer), caso seja necessário identificar rapidamente todos os locais no código que dependem da largura da placa.

Veja também este post anterior do SE.SE para uma discussão sobre como (ou não) escolher nomes para números.

Como uma observação lateral, uma vez que foi discutido aqui nos comentários : se, no código do seu programa real, importa se a variável ise refere a linhas e jcolunas do quadro, ou vice-versa, é recomendável escolher nomes de variáveis ​​que faça a distinção clara, como rowe col. O benefício de tais nomes é que eles fazem o código errado parecer errado.

Doc Brown
fonte
22
Eu gostaria de acrescentar que, se o OP tivesse escrito um ótimo mecanismo de xadrez, desejasse expandi-lo para incluir xadrez 3D ou outras variantes, então quais 8s precisam ser alterados será um desafio.
9788 Steve Barnes #
32
@SteveBarnes: Eu tento evitar esses argumentos, pois isso leva muito facilmente a um argumento revertido como "Eu acho que é extremamente diferente de fazer deste programa um tipo diferente de jogo, para que eu possa deixar o número mágico 8 onde ele está. "
Doc Brown
4
@IllusiveBrian Esse é um argumento clássico do "homem da palha" por dois motivos: (1) apenas definir uma constante não significa que o programador ainda não pode digitar "8" (por acidente, design ou malícia!) E (2) só porque existe uma constante BOARD_WIDTH = 8 não faz da BOARD_WIDTH a constante correta a ser usada em um determinado local do programa.
alephzero
3
@Blrfl ... ou levará à engenharia excessiva do código. É responsabilidade do desenvolvedor perceber o que pode mudar no futuro e codificar de acordo. Codificar como requisitos não muda causa problemas, mas o outro extremo não é melhor.
Malcolm
4
@corsiKa Quando as variáveis ​​de loop correspondem a eixos físicos, devem ser nomeadas x, y ou row, col ou, no xadrez, file, rank.
user949300
11

Ok, aqui estão alguns comentários que tenho:

Livrar-se dos números mágicos é uma ótima idéia. Existe um conceito conhecido como DRY, que geralmente é deturpado, mas a idéia é que você não duplique o conhecimento dos conceitos em seu projeto. Portanto, se você tiver uma classe chamada ChessBoard, poderá manter uma constante chamada BOARD_SIZE ou ChessBoard.SIZE anexada a ela. Dessa forma, existe uma única fonte para essas informações. Além disso, isso ajuda a legibilidade mais tarde:

for (int i = 0; i < ChessBoard.SIZE; i++){
  for (int j = 0; j < ChessBoard.SIZE; j++){

Mesmo que o número nunca mude, seu programa é sem dúvida melhor. Qualquer pessoa que o leia sabe mais informações sobre o que o código está fazendo.

Um nome ruim é pior que nenhum nome, mas isso não significa que algo não deve ser nomeado. Apenas mude o nome. Não jogue fora o bebê com a água do banho. : p O nome pode ser descritivo, desde que você entenda bem o que está descrevendo. Então, esse conceito pode ser usado para várias coisas diferentes.

desflores
fonte
8
isso parece meramente repetir os pontos já feitos e explicados na resposta superior #
306
-7

O que você realmente deseja é eliminar as referências ad nauseum às constantes, sejam elas nomeadas ou nuas:

for_each_chess_square (row, col) {
  /*...*/
}

Se você realmente proliferar a constante repetindo esses loops e outros enfeites, é melhor ficar com ele 8.

8é auto-descritivo; não é uma macro que representa outra coisa.

Você nunca vai transformá-lo em um programa de xadrez 9x9 e, se o fizer, a proliferação de 8 não será a maior dificuldade.

Podemos pesquisar uma base de código de 150.000 linhas para o token 8 e classificar quais ocorrências significam o que em segundos.

Muito mais importante é modularizar o código para que o conhecimento do xadrez se concentre no menor número de lugares possível. É melhor ter um, dois, talvez três módulos específicos de xadrez nos quais ocorra um 8 literal, do que trinta e sete módulos ligados a responsabilidades específicas de xadrez, referindo-se a 8 através de um nome simbólico.

Quando ou se essa constante se tornar uma fonte de tensão em seu programa, você poderá corrigi-lo facilmente naquele momento. Corrija problemas reais que estão acontecendo agora. Se você não sente que está sendo prejudicado por esse 8 em particular, siga esse instinto.

Suponha que no futuro você queira oferecer suporte a dimensões alternativas da placa. Nesse caso, esses loops terão que mudar se eles usam uma constante nomeada ou 8, porque as dimensões serão recuperadas por alguma expressão como board.widthe board.height. Se você tiver, em BOARD_SIZEvez de 8, esses lugares serão mais fáceis de encontrar. Então isso é menos esforço. No entanto, você não deve esquecer-se sobre o esforço de substituir 8com BOARD_SIZEem primeiro lugar. O esforço geral não é menor. Fazer uma passagem sobre o código para alterar 8e BOARD_SIZE, depois, outra para dar suporte a dimensões alternativas, não é mais barato do que apenas passar 8para o suporte de dimensão alternativa.

Também podemos analisar isso a partir de uma análise de risco / benefício puramente fria e objetiva. O programa tem constantes nuas agora. Se estes são substituídos por uma constante, não há benefício; o programa é idêntico. Com qualquer alteração, há um risco diferente de zero. Nesse caso, é pequeno. Ainda assim, nenhum risco deve ser assumido sem um benefício. Para "vender" a mudança em face desse raciocínio, temos a hipótese de um benefício: um benefício futuro que ajudará em um programa diferente: uma versão futura do programa que não existe agora. Se um programa desse tipo estiver sendo planejado, essa hipótese e seu raciocínio associado são de boa-fé e devem ser levados a sério.

Por exemplo, se você está a dias de adicionar mais código que proliferará ainda mais essas constantes, convém acabar com elas. Se essas instâncias das constantes são aproximadamente todas as instâncias que existirão, por que se preocupar?

Se você trabalhar em software comercial, os argumentos de ROI também serão aplicados. Se um programa não estiver sendo vendido e a alteração de alguns números codificados para constantes não melhorar as vendas, você não será compensado pelo esforço. A mudança tem retorno zero sobre o investimento de tempo. Os argumentos de ROI generalizam além do dinheiro. Você escreveu um programa, investindo tempo e esforço, e conseguiu algo com isso: esse é seu retorno, seu "R". Se, ao fazer essa alteração sozinha, você obtém mais desse "R", seja o que for, então, por todos os meios. Se você tem algum plano para desenvolvimento adicional, e essa alteração melhora o seu "R", o mesmo. Se a mudança não tiver um "R" imediato ou previsível para você, esqueça.

Kaz
fonte
13
8NÃO é tão auto-descritivo, é um número que pode significar qualquer coisa. E talvez eles queiram fazer um jogo de xadrez 9x9, por que não? Se você tem 150.000 linhas de código, não há como memorizar o que cada um 8significa no código e, mesmo que pudesse, por que você faria? Isso é apenas um monte de problemas extras. No que diz respeito à modularização, substituir 8por algo como NUM_RANKSnão prejudica a modularidade, na verdade, ajuda porque torna o código mais fácil de mexer.
precisa saber é o seguinte
4
@ Kaz Também faço isso. E então introduzo erros estranhos significativos, porque alguns usos não eram o que eu pensava, porque é difícil prestar muita atenção a um grande número de substituições e também estar sempre correto. Por esse motivo, evito entrar nesse cenário em primeiro lugar, por isso não posso apoiar conselhos que justifiquem a entrada nele.
Doppelgreener
11
"No entanto, você não deve esquecer-se sobre o esforço de substituir 8com BOARD_SIZE, em primeiro lugar" - O tempo que você tinha passado examinando ao longo do seu código tentando descobrir o que cada um 8faz em seus meses de programa a partir de agora provavelmente exceder o tempo gasto substituir 8com uma constante significativa no nível do módulo.
Christian Dean
11
@doppelganger Esse cenário já está aqui. Não estou dizendo, pegue um programa de xadrez que use constantes e substitua-o por 8. Nem estou dizendo: "planeje não usar constantes em um programa de xadrez ainda não escrito"
Kaz,
11
@Kaz Por que você gostaria de ler todo o código em torno do 8 e talvez às vezes entenda errado o contexto quando você pode passar mais 3 segundos dando um nome significativo? Quanto a saber o valor real da constante, é muito mais fácil olhar para cima o valor exato para uma variável que tentar para trás-engenheiro que algum inteiro magia nos meios de código
Jerfov2