Visibilità del prodotto di implementazione di fabbrica astratta

4

Sto implementando una fabbrica come questa:

public interface IMyProduct
{
    void DoSomething();
}

public interface IMyFactory
{
    IMyProduct CreateMyProduct( string aParameter );
}

internal MyFactory : IMyFactory
{
    public MyFactory( ISomeService someService )
    {
        _someService = someService;
    }

    public IMyProduct CreateMyProduct( string aParameter )
    {
        return new MyProduct( _someService, aParameter );
    }

    private readonly ISomeService _someService;
    private class MyProduct : IMyProduct
    {
        public MyProduct( ISomeService someService, string aParameter )
        {
            // use the service and the parameter for something...
        }

        void IMyProduct.DoSomething()
        {
            // whatever...
        }
    }
}

Cioè, ho implementato il prodotto all'interno dell'implementazione della fabbrica.

L'idea alla base di questo è che MyProduct è riferito solo all'interfaccia IMyProduct (perché l'interfaccia di fabbrica espone solo l'interfaccia del prodotto), quindi non ha bisogno di essere internal o anche public .

Certo, la fabbrica diventa più complicata, ma senza la classe del prodotto annidata, è fondamentalmente banale. La mia fabbrica è come un'implementazione complicata del costruttore per il prodotto, tutto è interessante nel prodotto (anche il nome del file potrebbe essere MyProduct.cs ).

Ora la domanda: qual è il trucco? Non ho mai visto una fabbrica astratta implementata in questo modo, e mi sto chiedendo quale sia il problema o il rischio che mi sto perdendo qui.

Modifica

Io pongo la domanda in modo diverso: perché i prodotti delle fabbriche apparentemente non sono mai implementati come classi private? e qual è il vantaggio di rendere l'implementazione del prodotto di una fabbrica accessibile a chiunque oltre a la fabbrica stessa?

    
posta Haukinger 05.03.2016 - 15:02
fonte

3 risposte

2

Questo è uno schema piacevole e standard se si desidera controllare la creazione di IMyProduct .

Non riesco a vedere alcun problema nel codice oltre alla strana formattazione attorno alla parentesi:).

Un paio di cose a cui pensare:

  • L'interfaccia è pubblica, quindi chiunque può implementarla e creare un tipo che non obbedisce alle regole di creazione:
public class EvilProduct : IMyProduct
{
    ... not doing the creation dance.
}
  • Una classe (astratta) con ctor internal Foo(ISomeService someService, string aParameter) è un'alternativa.
  • Assicurati di risolvere un problema reale aggiungendo questo in modo che non si tratti solo di pattern-wanking e overengineering:).
risposta data 05.03.2016 - 15:58
fonte
2

Pongo una domanda opposta: perché dovrebbe essere una classe annidata?

L'unico vero vantaggio di avere una classe nidificata è consentire a quella classe di accedere alle proprietà private del suo genitore. Ciò consente un controllo dell'accesso leggermente più stretto rispetto ad altre opzioni.

Nel tuo caso, non vedo MyProduct che chiama i dati personali della fabbrica. In quanto tale, non vedo il punto di tenerlo all'interno della classe di fabbrica. Se non vuoi che altri utenti creino le istanze di MyProduct , renderlo interno dovrebbe fornire una protezione sufficientemente grande (supponendo che i tuoi progetti siano strutturati correttamente).

Il motivo per cui non lo vedi spesso è perché i prodotti in genere non hanno bisogno di accedere ai dati personali della loro fabbrica.

Dall'altro lato, non vedo nulla di sbagliato nel mantenerlo come in questo momento.

    
risposta data 07.03.2016 - 11:21
fonte
1

Il punto di una classe Factory è quello di disaccoppiare la costruzione di una classe dalla sua implementazione. Questo ci permette di creare diverse implementazioni senza sapere molto sulle loro implementazioni concrete.

Quello che hai qui è simile, ma non identico. La tua fabbrica è legata a una specifica implementazione concreta e nasconde principalmente la sua logica di istanziazione come facciata opaca. Si ottiene l'interfaccia più flessibile di una fabbrica piuttosto che l'esposizione di un costruttore (i vettori possono sia creare un'istanza nuova o lanciare, non restituire un'istanza memorizzata nella cache o null), ma senza la sostituibilità delle fabbriche reali.

Mi sembra, quindi, che quello che vuoi sia una Fabbrica legata a un Tipo specifico - e fortunatamente, abbiamo esattamente un tale schema, che è il Metodo di Fabbrica. Invece di avere un oggetto Factory, hai semplicemente un costruttore privato e un metodo statico CreateProduct che nasconde la tua logica di costruzione e restituisce una nuova istanza - o un'istanza memorizzata nella cache o null.

public class MyProduct : IMyProduct
{  
    public static MyProduct CreateInstance (string aParameter)
    {
        return new MyProduct(...);
    }

    private MyProduct( ISomeService someService, string aParameter )
    {
    }           
}

Torna alla domanda

Per quanto riguarda la tua domanda essenziale - qual è il trucco ? Complessità. Se tutto ciò che vuoi è nascondere i dettagli della costruzione, nascondi i dettagli della costruzione! Aggiungere una seconda classe che implementa una seconda interfaccia solo per creare la tua classe specifica, concreta è più complicata, richiede più codice da implementare (più codice == più bug!) Ed è più complicato per l'utente della tua libreria (anche se sei tu - in 3 mesi == un altro utente).

    
risposta data 06.03.2016 - 17:05
fonte

Leggi altre domande sui tag