Domanda

Supponi di avere una classe chiamata PermissionManager che dovrebbe esistere una sola volta per il mio sistema e sostanzialmente svolge la funzione di gestione di varie autorizzazioni per varie azioni nella mia applicazione. Ora ho un po 'di classe nella mia applicazione che deve essere in grado di verificare una determinata autorizzazione in uno dei suoi metodi. Il costruttore di questa classe è attualmente pubblico, ovvero utilizzato dagli utenti API.

Fino a un paio di settimane fa, avrei semplicemente dovuto chiamare la mia classe il seguente pseudo-codice da qualche parte:

     PermissionManager.getInstance().isReadPermissionEnabled(this)

Ma dal momento che ho notato che tutti qui odiano i singleton + questo tipo di accoppiamento, mi chiedevo quale sarebbe stata la soluzione migliore, poiché gli argomenti che ho letto contro i singleton sembrano avere un senso (non testabile, accoppiamento elevato, ecc.) .

Quindi dovrei effettivamente richiedere agli utenti API di passare un'istanza di PermissionManager nel costruttore della classe? Anche se voglio solo una singola istanza di PermissionManager per la mia applicazione?

O sto sbagliando tutto e dovrei avere un costruttore non pubblico e una fabbrica da qualche parte che passa per me nell'istanza di PermissionManager?


Informazioni aggiuntive Nota che quando dico "Iniezione di dipendenza", sto parlando della DI Pattern ... Non sto usando alcun framework DI come Guice o Spring. (... ancora)

È stato utile?

Soluzione

Se si utilizza un framework di iniezione di dipendenza, il modo comune per gestirlo è passare un oggetto PermissionsManager nel costruttore o disporre di una proprietà di tipo PermissionsManager impostata dal framework.

Se ciò non è fattibile, fare in modo che gli utenti ottengano un'istanza di questa classe tramite factory è una buona scelta. In questo caso, la factory passa PermissionManager al costruttore quando crea la classe. All'avvio dell'applicazione, devi prima creare il singolo PermissionManager, quindi creare la tua fabbrica, passando il PermissionManager.

Hai ragione sul fatto che è normalmente ingombrante per i clienti di una classe sapere dove trovare l'istanza corretta di PermissionManager e passarla (o anche preoccuparsi del fatto che la tua classe usi un PermissionManager).

Una soluzione di compromesso che ho visto è quella di assegnare alla tua classe una proprietà di tipo PermissionManager. Se la proprietà è stata impostata (diciamo, in un unit test), si usa quell'istanza, altrimenti si usa il singleton. Qualcosa del tipo:

PermissionManager mManager = null;
public PermissionManager Permissions
{
  if (mManager == null)
  {
    return mManager;
  }
  return PermissionManager.getInstance();
}

Naturalmente, a rigor di termini, il tuo PermissionManager dovrebbe implementare una sorta di interfaccia IPermissionManager e questo è a cui l'altra classe dovrebbe fare riferimento in modo che un'implementazione fittizia possa essere sostituita più facilmente durante il test.

Altri suggerimenti

Puoi effettivamente iniziare iniettando il PermissionManager. Ciò renderà la tua classe più testabile.

Se ciò causa problemi agli utenti di quella classe, puoi farli usare un metodo factory o una factory astratta. Oppure puoi aggiungere un costruttore senza parametri che per loro chiamare che inietta il PermissionManager mentre i tuoi test usano un altro costruttore che puoi usare per deridere il PermissionManager.

Il disaccoppiamento delle classi rende più flessibili le lezioni, ma può anche renderle più difficili da usare. Dipende dalla situazione di cui avrai bisogno. Se hai un solo PermissionManager e non hai problemi a testare le classi che lo usano, non c'è motivo di usare DI. Se desideri che le persone possano aggiungere la propria implementazione di PermissionManager, DI è la strada da percorrere.

Se stai sottoscrivendo il modo di fare l'iniezione di dipendenza per fare le cose, qualunque classe abbia bisogno del tuo PermissionManager dovrebbe essere iniettato come un'istanza di oggetto. Il meccanismo che controlla la sua istanza (per imporre la natura singleton) funziona a un livello superiore. Se si utilizza un framework di iniezione delle dipendenze come Guice, può eseguire il lavoro di imposizione. Se stai eseguendo manualmente il cablaggio dell'oggetto, l'iniezione di dipendenza favorisce il raggruppamento del codice che esegue l'istanza (lavoro di un nuovo operatore) lontano dalla tua logica aziendale.

Ad ogni modo, comunque, il classico "capital-S" Singleton è generalmente visto come un anti-pattern nel contesto dell'iniezione di dipendenza.

Questi post sono stati perspicaci per me in passato:

  

Quindi dovrei effettivamente richiedere agli utenti API di passare un'istanza di PermissionManager nel costruttore della classe? Anche se voglio solo una singola istanza di PermissionManager per la mia applicazione?

Sì, questo è tutto ciò che devi fare. Se una dipendenza è un singolo / per richiesta / per thread o un metodo factory è la responsabilità del contenitore e della configurazione. Nel mondo .net avremmo idealmente la dipendenza da un'interfaccia IPermissionsManager per ridurre ulteriormente l'accoppiamento, presumo che questa sia la migliore pratica anche in Java.

Il modello singleton non è male da solo, ciò che lo rende brutto è il modo in cui viene comunemente usato, poiché è il requisito di volere solo una singola istanza di una certa classe, che penso sia un grosso errore.

In questo caso renderei PermissionManager una classe statica a meno che per qualsiasi motivo non sia necessario che sia un tipo installabile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top