Onde você deve colocar constantes e por quê?


34

Em nossas aplicações majoritariamente grandes, geralmente temos apenas alguns locais para "constantes":

  • Uma classe para GUI e concorrentes internos (títulos da guia, títulos da caixa de grupo, fatores de cálculo, enumerações)
  • Uma classe para tabelas e colunas de banco de dados (esta parte é código gerado) mais nomes legíveis para eles (atribuídos manualmente)
  • Uma classe para mensagens de aplicativos (log, caixas de mensagens etc.)

As constantes são geralmente separadas em estruturas diferentes nessas classes. Em nossos aplicativos C ++, as constantes são definidas apenas no arquivo .h e os valores são atribuídos no arquivo .cpp.

Uma das vantagens é que todas as strings etc. estão em um local central e todos sabem onde encontrá-las quando algo deve ser alterado.

Isso é especialmente algo que os gerentes de projeto parecem gostar quando as pessoas vão e vêm e, dessa forma, todos podem mudar coisas tão triviais sem precisar se aprofundar na estrutura do aplicativo.

Além disso, você pode alterar facilmente o título de caixas de grupo / páginas de guias semelhantes, etc., de uma só vez. Outro aspecto é que você pode imprimir essa classe e entregá-la a um não programador que pode verificar se as legendas são intuitivas e se as mensagens para o usuário são muito detalhadas ou muito confusas, etc.

No entanto, vejo algumas desvantagens:

  • Cada classe é fortemente acoplada às constantes
  • Adicionar / Remover / Renomear / Mover uma constante requer recompilação de pelo menos 90% do aplicativo (Nota: Alterar o valor não, pelo menos para C ++). Em um de nossos projetos C ++ com 1500 classes, isso significa cerca de 7 minutos de tempo de compilação (usando cabeçalhos pré-compilados; sem eles, são cerca de 50 minutos) mais 10 minutos de vinculação a determinadas bibliotecas estáticas.
  • A criação de uma versão com velocidade otimizada por meio do Visual Studio Compiler leva até 3 horas. Não sei se a enorme quantidade de relações de classe é a fonte, mas pode muito bem ser.
  • Você é levado a seqüências temporariamente codificadas diretamente no código, porque deseja testar algo muito rapidamente e não deseja esperar 15 minutos apenas para esse teste (e provavelmente todos os subsequentes). Todo mundo sabe o que acontece com os pensamentos "Eu vou consertar isso depois".
  • Reutilizar uma classe em outro projeto nem sempre é fácil (principalmente devido a outros acoplamentos apertados, mas o manuseio de constantes não facilita).

Onde você armazenaria constantes assim? Que argumentos você levaria para convencer seu gerente de projeto de que existem melhores conceitos que também cumprem as vantagens listadas acima?

Sinta-se à vontade para fornecer uma resposta específica ou independente de C ++.

PS: Eu sei que essa pergunta é meio subjetiva, mas sinceramente não conheço nenhum lugar melhor do que este site para esse tipo de pergunta.

Atualização sobre este projeto

Tenho novidades sobre o tempo de compilação:
Após as postagens de Caleb e gbjbaanb, divido meu arquivo de constantes em vários outros arquivos quando tive tempo. Também acabei dividindo meu projeto em várias bibliotecas, o que agora era possível muito mais fácil. Compilar isso no modo de liberação mostrou que o arquivo gerado automaticamente que contém as definições do banco de dados (tabela, nomes de colunas e mais - mais de 8000 símbolos) e cria certos hashes causou enormes tempos de compilação no modo de liberação.

Desativar o otimizador do MSVC para a biblioteca que contém as constantes do banco de dados agora nos permitiu reduzir o tempo total de compilação do seu projeto (vários aplicativos) no modo de liberação de até 8 horas para menos de uma hora!

Ainda precisamos descobrir por que a MSVC tem dificuldade em otimizar esses arquivos, mas, por enquanto, essa mudança alivia muita pressão, pois não precisamos mais confiar apenas nas versões noturnas.

Esse fato - e outros benefícios, como acoplamento menos rígido, melhor reutilização etc. - também mostraram que gastar tempo dividindo as "constantes" não era uma má idéia, afinal ;-)

Update2

Como essa pergunta ainda recebe alguma atenção:
Aqui está o que tenho feito nos últimos anos:

Coloque todas as constantes, variáveis ​​etc. exatamente no escopo relevante: se você usar uma constante apenas em um único método, não há problema em defini-la nesse método. Se uma única classe estiver interessada nela, deixe-a como um detalhe de implementação privada dessa classe. O mesmo se aplica ao espaço para nome, módulo, projeto, escopo da empresa. Eu também uso o mesmo padrão para funções auxiliares e similares. (Isso pode não se aplicar 100% se você desenvolver uma estrutura pública.)

Isso aumenta a capacidade de reutilização, testabilidade e capacidade de manutenção em um nível em que você gasta menos tempo compilando (pelo menos em C ++), mas também menos tempo em correções de bugs, o que lhe dá mais tempo para o desenvolvimento de novos recursos. Ao mesmo tempo, o desenvolvimento desses recursos será mais rápido, pois você poderá reutilizar mais códigos com mais facilidade. Isso supera qualquer vantagem que o arquivo de constantes centrais possa ter por uma magnitude.

Dê uma olhada especialmente no Princípio de Segregação de Interface e no Princípio de Responsabilidade Única, se você quiser saber mais.

Se você concorda, avalie a resposta de Caleb, pois essa atualização é basicamente uma visão mais geral do que ele disse.


2
pessoalmente, eu não teria o título da interface do usuário ou as seqüências de mensagens em constantes. Eu tê-los em app.config
jk.

1
Eu meio que gosto de como você está fazendo isso agora - eu entendo suas desvantagens, mas talvez tenhamos que lidar com isso.
bigtang

1
Eu já vi exatamente o mesmo problema em um grande projeto Java ... Uma interface enorme de "constantes" muda tudo e aguarda 15 minutos para que o eclipse recompile. Estou com Caleb: agrupe as constantes onde elas pertencem naturalmente, próximas ao código que as usa. Mantê-los separados porque são constantes é um exercício inútil do TOC.
22612 Michael Borgwardt

O acoplamento apertado não é um problema, porque é isso que você realmente deseja. Você deseja que uma alteração no seu arquivo constante afete possivelmente muitos arquivos de origem. (Claro que você também tem outros problemas).
gnasher729 24/08

@ gnasher729 isso só é verdade se muitas classes usarem a mesma constante. Você nunca desejaria que as classes fossem fortemente acopladas às constantes com as quais não estão relacionadas. Pode não parecer um problema no começo até você tentar reutilizá-lo em outro projeto sem copiá-lo ou executar testes isolados
Tim Meyer

Respostas:


29

As constantes específicas de uma classe devem ir na interface dessa classe.

As constantes que são realmente opções de configuração devem fazer parte de uma classe de configuração. Se você fornecer acessadores para as opções de configuração nessa classe (e usá-las no lugar das constantes em outro lugar), não será necessário recompilar o mundo inteiro ao alterar algumas opções.

As constantes compartilhadas entre as classes, mas que não devem ser configuráveis, devem ter um escopo razoável - tente dividi-las em arquivos que tenham usos específicos, para que as classes individuais incluam apenas o que realmente precisam. Isso novamente ajudará a reduzir o tempo de compilação quando você altera algumas dessas constantes.


1
Caso você esteja interessado: Atualizei minha pergunta com o que consegui, seguindo sua resposta e outras #
Tim Meyer

6

Eu diria simplesmente que você deseja dividir sua classe de constantes enormes em muitos arquivos menores, um por formulário, por exemplo. Isso garante que você não tenha uma dependência tão grande do arquivo constante; portanto, adicionar ou atualizar uma string não exigiria uma recompilação total. Você ainda pode armazenar esses arquivos em um local central, mas (por exemplo) possui 1 arquivo com constantes para cada caixa de diálogo, adequadamente nomeado. Em seguida, você pode incluir apenas esses arquivos nos arquivos de diálogo relevantes, o que reduz massivamente a recompilação.

Eu também sugiro que você use algo como os utilitários GNU GetText para lidar com as strings, ele foi projetado para tradução, mas funciona tão bem quanto simplesmente para alterar o texto para outra coisa. Você pode colocá-los em um recurso de strings, mas acho que eles são mais difíceis de trabalhar, pois são codificados por ID, os utilitários GetText são codificados por uma string original - torna as coisas muito fáceis de desenvolver.


Eu posso tentar. Como a classe de constantes com títulos etc é dividida em várias estruturas, eu poderia fazer uma classe por estrutura como começo. Não temos certeza sobre a questão do GNU, geralmente não queremos mudar nossas strings em tempo de execução, apenas durante o tempo de desenvolvimento. No entanto, estamos usando o mecanismo de tradução do Qt, caso tenhamos que traduzir para outro idioma no futuro.
Tim Meyer

Caso esteja interessado: Atualizei minha pergunta com o que consegui, seguindo sua resposta e outras #
Tim Meyer

2

Nota: Não sou desenvolvedor de C ++ ... mas aqui está o meu pensamento: Você deve considerar o seguinte comentário de @ jk sobre a diferença entre o uso de arquivos de configuração. No DotNet, existem arquivos de recursos usados ​​para armazenar essas informações. No Windows Forms, um arquivo de recurso é mantido do VS para cada formulário.

Não vejo um valor para uma constante ser colocada fora do seu escopo de uso, a menos que seja uma constante global que deve ser compartilhada. Como você mencionou, isso será difícil de manter durante o desenvolvimento, pelo menos. Além disso, você pode obter conflitos de nome. Outra coisa é que pode ser difícil saber quem está usando uma determinada constante.

Agora, se você quiser que não-programadores revisem as informações, na GUI, capture a tela para eles. Se você desejar que eles revisem as entradas da tabela de dados, poderá exportar os dados para o Excel ou algo semelhante.

Se você ainda deseja usar uma abordagem de localização centralizada e deseja colocar todas as suas constantes em um arquivo grande, cada desenvolvedor pode usar um arquivo compartilhado que é atualizado no final de cada intervalo para um arquivo central. Os dados virão de arquivos individuais usados ​​no desenvolvimento. Isso pode ser facilmente automatizado ou feito manualmente. No entanto, como eu disse, é provavelmente um risco que você não precisa correr.


2

Não há solução geral. Pergunte a si mesmo sobre desempenho, usabilidade, segurança e ciclo de vida de uma constante.

Quanto mais próximos forem definidos de seu escopo, maior será o desempenho.

Quanto mais eles são agrupados logicamente e fora de seu escopo, maior a reutilização.

Quanto menos acessíveis os clientes, maior a segurança.

Quanto maior o tempo de vida de uma constante, menos importa onde você coloca a sua utilidade.

Uma constante como um número de versão será definida em algum tipo de manifesto. Um código de erro de uma função de erro será definido dentro da classe. O código de erro provavelmente é algo com uma vida útil alta (= quase nunca muda). Colocá-lo em um arquivo constante apenas gera spams no arquivo com coisas desnecessárias.

Quanto menos a constante tiver o caráter de uma constante, mas uma variável (como número da versão), mais você poderá colocá-la fora. Quanto menor a variável, então, quanto mais constante, mais ela deve ser colocada dentro do escopo. Durante a depuração, faz sentido colocá-lo fora para reduzir o tempo de compilação.

No entanto, seu problema inicial é o tempo de compilação. Portanto, a questão é se você faz a pergunta certa. Se o tempo de compilação do seu aplicativo for muito alto, você deve pensar em uma maneira de torná-lo mais modular para que as peças funcionem independentemente umas das outras. Compile-o parcialmente e teste suas coisas independentemente. Se seus testes de unidade são realizados corretamente e em pleno andamento (o que é bastante trabalhoso), você pode facilmente mudar as coisas sem precisar se preocupar com isso. E então a pergunta ganha um impulso totalmente diferente.


1

Eu sugeriria colocar todas essas constantes em algum tipo de arquivo de configuração. Para aplicativos Java, geralmente usamos arquivos .properties, um texto simples com cada linha formatada como "(chave) = (valor)". Exemplo

MainPanel.Title = Bem-vindo ao nosso aplicativo
DB.table.users = TBL_USERS
logging.filename = application.log

Em seguida, você carrega esse arquivo no tempo de execução, preenche um cache que permite procurar uma chave e recuperar um valor. Quando você precisa de uma constante, você consulta o cache. Você ainda precisará ter suas chaves em algum lugar, e o cache precisará estar acessível globalmente, mas quando você altera os valores reais das constantes, nenhuma recompilação é necessária, deve ser possível reiniciar o aplicativo (ou se você realmente quer ter uma fantasia, ter vários arquivos e caches .properties e dar ao aplicativo a capacidade de recarregar um cache em tempo de execução).

Para uma implementação, encontrei esta pergunta do SO: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c (foi o primeiro hit em uma pesquisa no Google - eu não realmente usei esse software).


Para os projetos C ++, precisamos recompilar o arquivo constante apenas se alterarmos um valor. Isso ocorre porque os valores são atribuídos em um arquivo .cpp. No entanto a adição / remoção / renomeando / movendo um constante ainda requer uma reconstrução completa
Tim Meyer

@ TimMeyer: Se você dividir as constantes em vários arquivos, adicionar / remover uma constante afetaria apenas os arquivos que dependem desse arquivo específico, correto?
FrustratedWithFormsDesigner

Corrigir. O principal problema é que, se eu sugiro que as pessoas tendem a mencionar uma das coisas que eu listados como "vantagens" se perder
Tim Meyer

No entanto, eu realmente não tenho pensado sobre a colocação de arquivos de várias constantes no mesmo lugar
Tim Meyer

+1. @ Tim Meyer: Para esse propósito, a verificação em tempo de compilação custa muito mais do que economiza. Além disso, o que você fará se precisar internacionalizar?
Kevin cline
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.