Consiglia un approccio per disaccoppiare i costrutti di codice prodotto e accesso, al fine di semplificare la verifica del prodotto

3

Ho un codice che accoppia strettamente il codice specifico del prodotto e il codice di accesso al prodotto. Non sono sicuro di come districarlo. Ad esempio, per testare Prodotto, devo prendere in giro gli oggetti Access. E forse non c'è modo di aggirarlo, ma forse c'è. Ho un codice che va in questo modo:

class Product
{
    function __construct() {
        global $user;   //current logged in user object
        $id = $user->getId(); //used to make calls to DB
        $this->access = $db->query("SELECT access ... where id=$id");
        //DB returns "product access" information which is used to allow/reject access
        //code of product is tightly coupled with code for authorization
    }
}

class ProductA extends Product
{
    function __construct() {
        parent::__construct();
        if ($this->access != "all_access") doPartialAccess();
        else doFullAccess();
    }
}
//...
class ProductX extends Product
{
    function __construct() {
        parent::__construct();
        if ($this->access != "all_access") doPartialAccess();
        else doFullAccess();
    }
}

Approccio 1

Il mio primo pensiero è usare injection injection , per iniettare $user (o $access object direttamente) nel costruttore di Product . ma aspetta ... Dovrò farlo con ogni Product* estendendo Product . Anche se lo faccio, sarà effettivamente sufficiente spostare l'istanziazione di $user altrove nel codice (al di fuori delle classi Product* ). Come tale, non sono sicuro che sarà la soluzione migliore, perché ritengo che convolli il codice con l'introduzione di quelli che ritengo siano parametri non necessari nei costruttori, invece di rendere il codice base più gestibile.

Approccio 2

Un altro pensiero è di avere una sorta di "registro" per l'oggetto di accesso, in cui posso inserire oggetti e estrarre gli oggetti da esso. Ma aspetta, il modello di registro è considerato un anti-modello. E lo sto già usando - vedi global $user !? Non un modo orientato agli oggetti per farlo, bada bene, ma il concetto di registro tuttavia.

Domanda

Il mio obiettivo a lungo termine è quello di disaccoppiare il codice prodotto (vari calcoli, assemblare una stringa del modello di prodotto, ecc.) dal codice dei permessi utente che controlla l'accesso alla classe. I costrutti di accesso al prodotto sono effettivamente utilizzati per creare alcune query SQL, quindi il codice di prodotto e accesso è praticamente invischiato in questo momento. Il prodotto ha bisogno di accesso e non sono sicuro di poterli sbrogliare. Penso di non poter aggirare il fatto che Product abbia bisogno di Access . Forse ho solo bisogno di tirare fuori le linee di codice attualmente scarsamente posizionate che controllano l'accesso, e metterle nella loro propria classe di Access e chiamare quella classe ProductAccess , o qualcosa di simile. Potrebbe non esserci modo di aggirarlo, ma non sono del tutto sicuro di come farlo correttamente. DI sembra essere inappropriato (?)

Il mio obiettivo a breve termine è quello di voler testare il codice per ProductA , senza prendere in giro la classe di accesso. Ma questo potrebbe essere un obiettivo sbagliato da avere. Forse ho bisogno di prendere in giro Access, dal momento che testerò il prodotto senza sapere quale livello di accesso ha bisogno ...

Il mio obiettivo generale è trovare il modo di disaccoppiare le classi il più possibile, mantenendo le funzionalità esistenti e eliminando il modello di registro che ho ora. C'è un modo per farlo, o il mio obiettivo è imperfetto?

    
posta Dennis 18.12.2014 - 16:50
fonte

2 risposte

1

Penso che il problema sia che il tuo design di classe è un po 'confuso, quindi la tua domanda non ha una risposta semplice ...

  • Un prodotto non dovrebbe avere alcuna conoscenza degli utenti o delle autorizzazioni di accesso. Ad esempio, un report di inventario deve accedere a tutti i prodotti, indipendentemente da quali utenti hanno diritti di accesso.
  • Probabilmente dovresti considerare qualche forma di classe controller / manager che si preoccupa di chi ha i diritti di accesso ai prodotti (forse chiamato qualcosa come ProductAccessManager). Può chiedere alla classe di prodotto a quali ruoli utente è consentito accedere, quindi verificare che il richiedente sia in uno di questi ruoli e concedere o negare l'accesso a seconda dei casi. Questo separa la logica di accesso dal prodotto stesso.

Un design di classe pulito evita il tipo di domande che chiedi e in genere semplifica i test. Penso che qualche ulteriore riflessione sul design della tua classe pagherà grandi dividendi in futuro.

    
risposta data 19.01.2016 - 08:07
fonte
0

Per questo particolare problema, consiglierei qualcosa di simile al tuo secondo approccio.

Controlla lo schema di localizzazione del servizio ( Si tratta di un buon servizio di localizzazione, ed è questo il modello di localizzazione dei servizi (?) OK? ) questo è fondamentalmente quello che hai chiamato" registro ".

In secondo luogo - non mettere utente come servizio lì. Molto probabilmente hai bisogno di qualcosa come Session, e nel tuo codice fai qualcosa come

$ user = ServiceLocator :: get ("Session") - > currentUser ();

Direi che ServiceLocator è fondamentalmente Singleton reso mockable;)

E non dimenticare di coprire ServiceLocator con dei test;)

L'iniezione di dipendenza può essere utile o meno, a seconda della struttura. Per il tuo caso sembra che non si adatti - specialmente che $ user è globale, quindi generalmente si adatta alla descrizione di Singleton.

    
risposta data 22.02.2015 - 13:24
fonte

Leggi altre domande sui tag