Constantes são tão maléficas como variables ​​globais e singletons?

Ouvi muitas vezes neste fórum que usar variável global é um pecado morto e implementar singleton é um crime.

Acabei de me lembrar que velhas e boas constantes possuem todas as características dessas práticas desonradas: são acessadas globalmente e, sem dúvida, elas apresentam um estado global.

Então, a questão é: não devemos declarar uma jihad às constantes também, e usar todas as coisas modernas como DI, IoC ou outras palavras estilosas ao invés disso?

A principal razão pela qual variables ​​globais são consideradas má prática é porque elas podem ser modificadas em uma parte de um sistema e usadas em outra parte, sem vínculo direto entre essas duas partes de código.

Isso leva a potenciais erros porque é possível escrever código que usa uma variável global sem saber (ou considerar) todos os lugares onde ele é usado e maneiras pelas quais ele poderia ser alterado. Ou vice-versa, escreva um código que faça uma mudança para um global, sem perceber o impacto que a mudança pode ter em outras partes não relacionadas do seu código.

Constantes não compartilham esse problema, porque são … bem, constantes. Uma vez que eles são definidos, eles não podem ser alterados e, portanto, o emitido descrito no parágrafo acima não pode ocorrer.

Portanto, eles estão bem para usar globalmente.

Dito isto, eu vi algum código PHP mal escrito que usa define para criar constantes, mas declara as constantes de forma diferente em diferentes circunstâncias. Este é um mis-use de constantes: uma constante deve ser um valor absolutamente fixo; deve ser apenas um valor único. Se você tiver algo que poderia potencialmente ser valores diferentes em corridas diferentes através do programa, então não deve ser definido como uma constante. Esse tipo de coisa deve, de fato, ser uma variável, e então deve seguir as mesmas regras que outras variables.

Esse tipo de mis-use só pode acontecer em linguagem de script como PHP; não pode acontecer em uma linguagem compilada porque você só pode definir uma constante uma vez, em um lugar e em um valor fixo.

De um modo geral, sim, evite constantes. Eles introduzem o acoplamento dos consumidores ao escopo global. Ou seja, os consumidores dependem de algo afastado. Isso não é obvio, por exemplo,

 class Foo { public function doSomething() { if (ENV === ENV_DEV) { // do something this way } else { // do something that way } } } 

Sem conhecer os internos do doSomething , você não saberá que existe uma dependência no alcance global que tenha essa constante. Então, além de tornar seu código um pouco difícil de entender, você também está limitando como ele pode ser reutilizado.

O acima também é verdadeiro para constantes que possuem apenas um valor, por exemplo,

 public function log($message) { fwrite(LOGFILE, $message); } 

Aqui, a constante indicaria um recurso de arquivo definido em algum lugar fora como

 define('LOGFILE', fopen('/path/to/logfile')); 

E isso é tão obvio quanto o uso do ENV . É uma dependência que exige que exista algo fora da class. E eu tenho que saber isso para trabalhar com esse object. Uma vez que a class usando essa constante oculta esse detalhe, talvez eu tente registrar algo sem ter certeza de que a constante existe e então eu me pergunto por que isso não funciona. Nem precisa ser um recurso, LOGFILE poderia simplesmente conter o caminho como uma string. O mesmo resultado.

Confiar em constantes globais em seus consumidores também exigirá que você configure o estado global em suas unidades de teste. Isso é algo que você geralmente deseja evitar, mesmo que as constantes sejam de valor fixo, porque o ponto da unidade de teste é testar a unidade isoladamente e ter que colocar o ambiente em um determinado estado dificulta isso.

Além disso, o uso de constantes globais sempre representa a ameaça de constantes constantes de diferentes bibliotecas conflitantes. Como regra geral, não coloque nada no escopo global. Use namespaces para agrupar constantes se você precisar usá-las.

No entanto, note que as constantes com nomes de nomes ainda têm os mesmos problemas em relação ao acoplamento, assim como as constantes de class. Enquanto esse acoplamento estiver dentro do mesmo espaço de nome, ele é menos crítico, mas uma vez que você começa a se juntar a constantes de vários espaços de nome, você está dificultando novamente a reutilização. Para esse assunto, considere qualquer API pública de constantes.

Uma alternativa ao uso de constantes seria usar objects de valor imutáveis, por exemplo:

 class Environment { private $value; public function __construct($value) { $this->assertValueIsAllowedValue($value); $this->value = $value; } public function getValue() { // … 

Desta forma, você pode transmitir esses valores aos objects que precisam deles, além de se certificar de que os valores são válidos. Como sempre, YMMV. Esta é apenas uma opção. Uma única constante não fará seu código inutilizável, mas depender em grande parte das constantes terá um efeito prejudicial, de modo que, como regra geral, tente mantê-los no mínimo.

Em uma nota lateral relacionada, você também pode estar interessado em:

  • Prós e contras de constantes de interface e
  • PHP global em funções

Há uma grande diferença entre uma variável global e uma constante global.

O motivo principal por que uma variável global é evitada é porque pode ser modificado por qualquer coisa a qualquer momento. Ele pode apresentar todos os tipos de dependencies ocultas na ordem de chamada / execução e pode resultar em código idêntico funcionando às vezes e não em outros, dependendo de se e como o global foi alterado. Obviamente, o mau jogo pode ser aumentado ainda mais se você estiver lidando com simultaneidade ou paralelismo.

Uma constante global é (ou deve ser) exatamente a mesma em todo o seu código em todos os momentos. Uma vez que seu código começa a ser executado, é garantido que cada bit de visualização de código verá a mesma coisa toda vez. Isso significa que não há risco de introduzir dependencies acidentais. O uso de constantes pode, na verdade, ser muito bom para melhorar a confiabilidade, pois significa que você não precisa atualizar o valor em vários locais se precisar alterar seu código. (Nunca subestime o erro humano!)

Singletons é um outro problema. É um padrão de design freqüentemente abusado que basicamente pode acabar como uma versão orientada a objects de variables ​​globais. Em algumas línguas (como C ++), também pode ficar muito errado se você não tiver cuidado com a ordem de boot. No entanto, pode ser um padrão útil na ocasião, embora geralmente haja melhores alternativas (embora, às vezes, exigem um pouco mais de trabalho).

EDITAR: Apenas para expandir brevemente, você mencionou na sua pergunta que as constantes globais apresentam o “estado global de sempre”. Isso não é realmente exato, porque uma constante global é (ou deve ser) corrigida da mesma maneira que o código fonte é corrigido. Ele define a natureza estática do programa, ao passo que o “estado” geralmente é entendido como um conceito dynamic de tempo de execução (ou seja, o material que pode mudar).