Use uma variável global, um singleton ou outra coisa

5

Prefácio: Estou trabalhando em PHP ( Abandono espero que todos vocês que entram aqui ).

Background: Existe um grande conjunto de funções globais no PHP, algumas das quais são chamadas de sistema diversas, como sleep (e outros). Agora, eu uso sleep (e outros) em um monte de scripts diferentes que eu corro em um monte de lugares diferentes, e descobri que preciso dormir para chamar pcntl_signal_dispatch assim que o sleep terminar, mas possivelmente não em todos os meus scripts.

Uma generalização: Eu preciso fazer com que a função global faça mais do que atualmente, espero não interromper muito o meu ecossistema atual.

Minha solução: achei que poderia criar uma classe de wrapper que executa o "sleep" correto.

Singleton: Eu poderia fazer uma singleton "System" classe que envolve as funções globais. Inferno, eu poderia até fazer os métodos estáticos wrappers. A desvantagem é que haveria muita verificação de clichê para ver qual versão eu precisaria executar, seja uma chamada de função baunilha ou uma com material extra.

Variável global: Eu poderia criar uma classe "System" genérica que envolve as funções globais. Eu poderia então estender a classe System com classes diferentes que substituem as funções do wrapper. Eu crio uma variável global System dentro de cada script, dependendo de como eu preciso que as funções se comportem. Todos os meus scripts têm acesso a essa variável global. A desvantagem é que eu teria que ter certeza de que a variável global fosse declarada, nunca é sobrescrita, e usa o bom System .

Outra coisa: Eu poderia criar uma classe SysControl com uma variável estática System e wrappers estáticos dos wrappers System das funções e, em seguida, trocar qual System my SysControl referências de classe. A desvantagem é que sinto que estou indo ao mar.

Há mais opções que eu deva considerar? Qual destes métodos é o melhor e por quê? Que armadilhas devo procurar daqui para frente?

EDIT: Acabei usando a solução Outra coisa .

    
por MirroredFate 15.04.2014 / 02:29
fonte

2 respostas

2

Prefácio

Para qualquer projeto, a resposta a essa pergunta provavelmente será diferente. Isso é simplesmente um resultado da estrutura e da filosofia geral. Pode ser fácil e direto em alguns casos, mas extremamente difícil e complicado em outros.

No entanto , se este for um problema difícil, isso é um cheiro de código muito strong: algo provavelmente está errado com o seu projeto. Dito isto, todos estivemos lá e muitas vezes não temos tempo para reescrever toda a base de código. Ainda assim, para começar: qual é o caminho certo para resolver isso?

O caminho certo

IoC (Inversão de Controle) é um padrão e princípio de design que tem se estabilizado ganhou tração. Basicamente, afirma que, em vez de construir o que precisam, os objetos solicitam uma instância do que precisam de algum contêiner IoC, o que pode fornecer a "coisa" apropriada.

Nesse caso, poderíamos fazer algo como o seguinte:

interface SystemAdapterInterface
{
    public function sleep($duration);
}

class WakefulSystemAdapter implements SystemAdapterInterface
{
    public function sleep($duration)
    {
        sleep($duration);
        pcntl_signal_dispatch();
    }
}

class SleepySystemAdapter implements SystemAdapterInterface
{
    public function sleep($duration)
    {
        sleep($duration)
    }
}

class SleepingEntity
{
    public function __construct(SystemAdapterInterface $system)
    {
        $this->system = $system
    }

    public function mayReceiveSignal()
    {
        $this->system->sleep(100000);
    }
}

// Register the system and sleeping entity with the IoC Container
// Using the LoEP Container for IoC
$container = new League\Container\Container;

$container->add('SystemAdapterInterface', 'WakefulSystemAdapter ');

$container
    ->add('SleepingEntity')
    ->withArgument('SystemAdapterInterface');

//Now we can easily get an instance of sleeping entity.
$container->get('SleepingEntity');

Neste exemplo, usamos um contêiner IoC para gerenciar o que System SleepingEntity usa. Minha recomendação para um IoC é Container , da Liga Extraordinária .

Esta solução mantém nosso SleepingEntity desacoplado de nosso System . Dependendo de onde usamos SleepingEntity , podemos simplesmente configurar nosso IoC Container de maneira diferente. É legal porque é relativamente simples, fácil de testar e permite expansão no futuro.

Infelizmente, isso não funciona muito bem se você já tem um monte de código com paradas (ou chamadas de função globais semelhantes) e precisa modificar o comportamento delas. Então, o que podemos fazer sobre isso?

O jeito não tão certo

Ok, então você quer fazer A coisa certa , mas o código está mal documentado, tem dependências entrelaçadas de todas as maneiras, e é um incrivelmente pesado a ser resolvido. já passou seu IoC para cada lado da árvore até que seja usado para instanciar o objeto certo, todas as vezes.

A melhor coisa a fazer é ser voltada para o futuro. Se você quiser tornar a base de código melhor, mais dissociada, dê o primeiro passo com essa adição.

Digamos que você tenha o seguinte:

class DoSomethingAndSleep
{
    public function foo($a, $b, $c)
    {
        $a->thing();
        $b->thing();
        sleep(5);
        $c->thing();
    }
}

Agora vamos fingir que DoSomethingAndSleep é usado em 1.000 lugares diferentes por todos os tipos de códigos diferentes. De fato, às vezes é instanciado anonimamente através de uma variável de classe, então encontrar esses 1.000 lugares é difícil. Realmente, muito difícil.

Bem. Vamos apenas mover a bondade do desacoplamento para DoSomethingAndSleep . Espero que você tenha tempo para refatorar as coisas uma a uma no futuro.

class DoSomethingAndSleep
{
    public function __construct(SystemAdapterInterface $system=null)
    {
        if ($system=== null)
        {
            $system = App::getIoC()->get('SystemAdapterInterface');
        }
        $this->system = $system;
    }

    public function foo($a, $b, $c)
    {
        $a->thing();
        $b->thing();
        $this->system->sleep(5);
        $c->thing();
    }
}

Ok, então o que está acontecendo aqui? Bem, em primeiro lugar, há um reconhecimento de que não temos a infraestrutura certa para passar o IoC para DoSomethingAndSleep ou para usá-lo para construir o DoSomethingAndSleep , então, em vez disso, vamos ignorar tudo isso e obtenha o "padrão" IoC de nosso App . Isso significa que, na inicialização do aplicativo, precisamos:

  • instanciar o contêiner IoC
  • registre o SystemAdapterInterface com o contêiner IoC
  • registre o contêiner IoC com o aplicativo

Isso é aceitável. Como temos um argumento padrão em nosso construtor DoSomethingAndSleep , podemos gastar o tempo avançando para refatorar as coisas para usar o IoC Container sem quebrar a compatibilidade com versões anteriores.

Ainda precisaremos migrar (esperançosamente) usando Container para gerenciar objetos e instâncias, mas pelo menos estamos em um ponto em que estamos registrando o SystemAdapterInterface e usando o registrado em vez de instanciar diretamente isso.

Se você estiver usando o League Container , podemos registrar DoSomethingAndSleep com nosso IoC Container usando esse método e aproveitar o Fiação Automática e o Delegado de Reflexão para cuidar dessa nova dependência para DoSomethingAndSleep .

$container = new League\Container\Container;

// register the reflection container as a delegate to enable auto wiring
$container->delegate(
    new League\Container\ReflectionContainer
);

$doSomethingAndSleep = $container->get('DoSomethingAndSleep');

Isso deve fazer com que nossa refatoração futura seja mais tranquila.

Conclusão

É sempre difícil descobrir como pegar uma base de código antiga e monolítica e, lentamente, torná-la melhor, mais fácil de manter. Você não pode quebrar a compatibilidade com versões anteriores, precisa avançar com novos recursos e deseja garantir que as adições melhorem as coisas. Isso é difícil. Eu acho que a melhor sugestão é esta: se você não pode escrever testes para isso, você terá um tempo terrível para mantê-lo. Então, toda vez que você fizer uma mudança, certifique-se de escrever um teste de unidade contra essa mudança.

Nesse caso, usar IoC faz isso com muito pouco esforço. Mesmo que seu projeto não esteja estruturado para usá-lo, você pode introduzi-lo inicialmente e usá-lo em um único lugar, e com o tempo você poderá refatorar seu código até que tudo esteja usando DI e é agradavelmente desacoplada.

    
por 22.06.2016 / 01:02
fonte
0

Are there any more options I should consider?

  • dê uma olhada nas constantes do php em vez das variáveis globais
  • considere o uso de campos de bits em vez de uma variável ou constante SYSTEM
por 19.06.2016 / 23:18
fonte