Utilizza una variabile globale, un singleton o qualcos'altro

5

Prefazione: sto lavorando in PHP ( lascia sperare a tutti quelli che entrano qui ).

Background: esiste un ampio set di funzioni globali in PHP, alcune delle quali sono chiamate di sistema varie, come sleep (e altri). Ora, uso sleep (e altri) in una serie di script diversi che eseguo in un sacco di diversi luoghi, e ho trovato che ho bisogno di dormire per chiamare pcntl_signal_dispatch non appena termina sleep ma forse non in tutti i miei script.

Una generalizzazione: ho bisogno di fare in modo che la funzione globale faccia più di quanto non faccia attualmente, si spera che senza interrompere troppo il mio attuale ecosistema.

La mia soluzione: immagino di poter creare una classe wrapper che esegua il "sleep" corretto.

Singleton: Potrei creare una classe "System" singleton che racchiuda le funzioni globali. Diavolo, potrei persino fare i metodi statici dei wrapper. Il rovescio della medaglia è che ci sarebbe un bel po 'di controlli per vedere quale versione avrei bisogno di eseguire, sia una chiamata di funzione vanilla che una con cose extra.

Variabile globale: Potrei creare una generica classe "Sistema" che avvolge le funzioni globali. Potrei quindi estendere la classe System con classi diverse che annullano le funzioni wrapper. Creo una variabile globale System all'interno di ogni script, a seconda di come ho bisogno che le funzioni si comportino. Tutti i miei script hanno accesso a quella variabile globale. Lo svantaggio è che dovrei assicurarmi che la variabile globale sia dichiarata, non venga mai sovrascritta e usi il giusto System .

Qualcos'altro: Potrei creare una classe SysControl con una variabile System statica e wrapper statici dei wrapper di System delle funzioni, e quindi scambiare quale System my SysControl riferimenti di classe. Il rovescio della medaglia è che sento che sto andando fuori bordo.

Ci sono altre opzioni che dovrei prendere in considerazione? Quale di questi metodi è il migliore e perché? Quali insidie dovrei cercare di andare avanti?

EDIT: ho finito per utilizzare la soluzione Qualcos'altro .

    
posta MirroredFate 15.04.2014 - 02:29
fonte

2 risposte

2

Prefazione

Per ogni progetto dato, la risposta a questa domanda sarà probabilmente diversa. Questo è semplicemente un risultato della struttura e della filosofia generale. Può essere facile e diretto in alcuni casi, ma estremamente difficile e complicato in altri.

Tuttavia , se questo è un problema difficile, si tratta di un odore di codice molto strong: è probabile che qualcosa non funzioni nel tuo progetto. Detto questo, siamo stati tutti lì, e spesso non abbiamo il tempo di riscrivere l'intera base di codice. Ancora, per iniziare: qual è il modo giusto per risolvere questo?

Il modo giusto

IoC (Inversion of Control) è un modello di progettazione e un principio che ha costantemente ha guadagnato la trazione. Fondamentalmente afferma che invece di oggetti che costruiscono ciò di cui hanno bisogno, richiedono un'istanza di ciò di cui hanno bisogno da qualche contenitore IoC, che può dare loro la "cosa" appropriata.

In questo caso, potremmo fare qualcosa di simile al seguente:

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');

In questo esempio, utilizziamo un contenitore IoC per gestire ciò che System SleepingEntity utilizza. La mia raccomandazione per un IoC è Container , dalla Lega dei pacchetti straordinari .

Questa soluzione mantiene il nostro SleepingEntity disgiunto dal nostro System . A seconda di dove usiamo SleepingEntity , possiamo semplicemente configurare il nostro IoC Container in modo diverso. È bello perché è relativamente semplice, facile da testare e consente l'espansione in futuro.

Sfortunatamente, questo non funziona particolarmente bene se hai già un sacco di codice con sleep (o chiamate di funzioni globali simili) e devi modificare il loro comportamento. Quindi cosa possiamo fare a riguardo?

The Not Quite as Right Way

Ok, quindi vuoi fare The Right Thing , ma il codice è scarsamente documentato, ha dipendenze che si intrecciano in ogni direzione, ed è un ascensore incredibilmente pesante da superare e assicurarti hai passato il tuo IoC in ogni direzione lungo l'albero fino a quando non viene utilizzato per istanziare l'oggetto giusto, ogni volta.

La cosa migliore da fare è essere lungimiranti. Se vuoi rendere il codebase migliore, più disaccoppiato, fai il primo passo con questa aggiunta.

Supponiamo di avere il seguente:

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

Ora facciamo finta che DoSomethingAndSleep sia usato 1000 diversi posti da tutti i tipi di codice diverso. Infatti, a volte viene istanziato in modo anonimo attraverso una variabile di classe, quindi trovare quei 1.000 posti è difficile. Davvero, davvero difficile.

Bene. Spostiamo la qualità del disaccoppiamento in DoSomethingAndSleep . Spero che tu abbia il tempo di ridefinire le cose una per una in 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, quindi cosa sta succedendo qui? In primo luogo, riconosciamo che non disponiamo dell'infrastruttura giusta per passare il IoC in DoSomethingAndSleep , o per usarlo per costruire il DoSomethingAndSleep , quindi, invece, ignoreremo tutto questo e ottieni il% "standard"% co_de dal nostro IoC . Ciò significa che all'avvio dell'applicazione, dobbiamo:

  • istanzia il contenitore IoC
  • registra SystemAdapterInterface con il contenitore IoC
  • registra il contenitore IoC con l'app

Questo è accettabile. Dato che abbiamo un argomento predefinito nel nostro costruttore di App , possiamo dedicare del tempo a spostare in avanti le attività di refactoring per utilizzare DoSomethingAndSleep senza rompere la compatibilità all'indietro.

Dovremo comunque passare a (si spera) usando IoC Container per gestire oggetti e istanze, ma almeno siamo al punto in cui stiamo registrando il Container e usando quello registrato invece di istanziare direttamente esso.

Se stai utilizzando SystemAdapterInterface , possiamo anche registrare League Container con il nostro DoSomethingAndSleep utilizzando questo metodo e sfruttare il Auto Wiring e il delegato di Reflection per occuparsi di questa nuova dipendenza per IoC Container .

$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');

Questo dovrebbe rendere il nostro futuro refactoring andare anche più agevolmente.

Conclusione

È sempre difficile capire come prendere un vecchio codice base monolitico e renderlo lentamente migliore, più gestibile. Non è possibile interrompere la compatibilità con le versioni precedenti, è necessario andare avanti con nuove funzionalità e assicurarsi che le aggiunte migliorino le cose. È difficile. Penso che il miglior suggerimento sia questo: se non puoi scrivere test per questo, avrai un tempo terribile per mantenerlo. Pertanto, ogni volta che apporti una modifica, assicurati di poter scrivere un test unitario contro tale modifica.

In questo caso, l'utilizzo di DoSomethingAndSleep lo fa con pochissimo sforzo. Anche se il tuo progetto non è strutturato per usarlo, puoi introdurlo inizialmente e usarlo solo in un posto, e poi col passare del tempo puoi rifattorizzare il tuo codice finché alla fine tutto sta usando DI ed è piacevolmente disaccoppiato.

    
risposta data 22.06.2016 - 01:02
fonte
0

Are there any more options I should consider?

  • dai un'occhiata a php costanti anziché a variabili globali
  • considera l'utilizzo di campi di bit invece di una variabile SYSTEM o costante
risposta data 19.06.2016 - 23:18
fonte

Leggi altre domande sui tag