Creazione oggetto: quando devo esporre una classe factory vs wrapping?

6

Sto riscontrando qualche problema nel tentativo di capire quando utilizzare una factory o una classe wrapper. Questa domanda è leggermente orientata verso C # credo, quindi non sono sicuro se questo è il posto giusto per chiedere.

Supponiamo che esista una libreria che espone un'interfaccia chiamata IStackExchangeClient e più implementazioni differenti di tale interfaccia (implementazioni private di cui i client non saranno a conoscenza), HttpStackExchangeClient , UdpStackExchangeClient .

Possiamo consentire la creazione dell'oggetto dell'interfaccia attraverso una factory, come StackExchangeClientFactory.Create() , oppure possiamo racchiuderlo in un'altra classe come StackExchangeClient che internamente fa la stessa cosa, ma semplicemente proxy.

Nella seconda istanza, i clienti possono scrivere new StackExchangeClient() che mi sembra molto più naturale. Quindi la mia domanda è, quando scelgo l'una o l'altra? Quali sono alcune delle considerazioni che dovrei prendere in considerazione?

Ci ho pensato molto e non posso scegliere tra uno stile o l'altro, ma chiaramente alcune parti del framework .NET usano le fabbriche, e alcune parti no, anche quando ci sono più implementazioni sottostanti di la sua interfaccia.

Grazie!

    
posta Yuki 26.12.2014 - 10:53
fonte

6 risposte

1

Se stai utilizzando l'iniezione di dipendenza, l'utilizzo del modello di fabbrica nasconde le dipendenze dell'oggetto che stai creando dal consumatore. Ad esempio:

class MyClass
{
    MyClass(IDependencyA dependencyA, IDependencyB dependency) { ... }
}

Se vuoi creare direttamente questo, fai:

var myClass = new MyClass(dependencyA, dependencyB);

Ciò significa che la classe che consuma deve prendere dipendenza da tutte le dipendenze di ciò che stai creando. Se MyClass ha un sacco di dipendenze, e viene creato un sacco di posti, quindi avere un MyClassFactory fornisce il disaccoppiamento:

class MyClassConsumer
{
    private readonly IMyClassFactory factory;

    MyClassConsumer(IMyClassFactory factory)
    {
        this.factory = factory;
    }

    ...

    private void someMethod()
    {
        var myClass = this.factory.Create();
    }
}

Come puoi vedere, MyClassConsumer è ora completamente disaccoppiato dalle dipendenze di MyClass . Se le dipendenze di MyClass cambiano, ora devi solo cambiare l'implementazione di Factory, e nient'altro cambia.

Considera questo: qualsiasi classe che chiama new è per definizione una fabbrica. Cercare di seguire il Principio di Responsabilità Unica significa che la creazione di un oggetto dovrebbe essere responsabilità di una classe di fabbrica, e dovrebbe essere l'unica responsabilità di quella classe.

    
risposta data 26.03.2015 - 13:00
fonte
1
 > Object creation: when should I expose a factory vs wrapping class?

Risposta dal punto di vista unità test :

Se vuoi fare test unitari e il test richiede di cambiare la creazione dell'oggetto degli oggetti figlio, factory (metodo o classe) è la strada da percorrere.

Se il tuo programma è disseminato di diversi IOrderItem orderItem = new OrderItem(...) , non puoi sostituire OrderItem con un falso.

Se il tuo programma utilizza il metodo factory IOrderItem orderItem = createOrderItem(..) è facile sostituire OrderItem con un falso reimplementando / beffando il metodo factory.

L'unico argomento pro wrapping-class a cui posso pensare è che è più intuitivo e più facile da scoprire per l'api-consumer come indicato nel commento di @Frank Hileman.

    
risposta data 23.09.2015 - 11:28
fonte
0

Utilizza una classe di wrapping quando l'interfaccia è privata, cioè l'utente non può creare le proprie nuove classi derivate. Questo di solito è un design piuttosto discutibile.

Altrimenti, rendi pubbliche le interfacce e le classi derivate.

Non utilizzare un metodo di fabbrica a meno che non abbia un vantaggio richiesto dalla lingua, ad es. deduzione di tipo generico, che puoi vedere in Tuple.Create.

    
risposta data 26.12.2014 - 11:08
fonte
0

Un factory consente la creazione di oggetti che richiedono un codice di configurazione complesso o un codice di installazione condiviso attraverso le implementazioni. Non si dovrebbe eseguire la stessa configurazione complessa in un costruttore in quanto un costruttore è progettato solo per inizializzare un oggetto e prepararlo per l'uso impostando le variabili membro.

L'implementazione di StackExchangeClientFactory.Create () consente di eseguire il codice di installazione condiviso tra le implementazioni, determinare l'implementazione più appropriata da utilizzare e quindi restituire l'implementazione. Nel wrapper dovresti eseguire lo stesso codice di installazione nel tuo costruttore, che dovrebbe essere evitato.

Inoltre, se stai usando DI (Dependency Injection), che dovresti, dovresti usare una Factory per restituire l'implementazione appropriata al framework DI. Il metodo Create sulla Factory applica la logica per determinare l'implementazione appropriata e restituisce quella.

    
risposta data 24.02.2015 - 12:08
fonte
0

Non capisco la tua domanda. E penso che ci siano dei malintesi:

I) Un fabbrica è un modello di progettazione utilizzato per separare la creazione dell'oggetto dal consumo dell'oggetto . Ci sono due modi possibili per affrontarlo:

1) Delega la creazione dell'istanza a un oggetto separato

2) Per creare istanze viene utilizzato un metodo statico

Il vantaggio: le dipendenze non vengono più create. L'oggetto che ha bisogno chiede per le sue dipendenze. Le fabbriche consentono l'iniezione di dipendenza . Ma invece di gestire una dozzina di fabbriche e tutto il cablaggio, lo lasci per un'astrazione chiamata (DI-) container .

II) Un'interfaccia è il modo C # / Java di gestire l'ereditarietà multipla. Invece di lasciare che l'oggetto erediti le implementazioni da più oggetti, puoi solo ereditare un'API, per così dire. Un'interfaccia è un contratto che definisce un'API e lascia l'implementazione all'oggetto. Gli oggetti definiscono famiglie di oggetti in qualche modo correlati tramite una relazione is-a , si potrebbe dire, le interfacce definiscono una relazione di oggetti diversi che condividono comportamento comune .

Che cosa significa per (I) ?

Se hai una fabbrica per oggetti diversi che seguono la stessa API , il tipo di ritorno della fabbrica è l'interfaccia comune ( pensa a ILogging con FileLogger , DatabaseLogger , EventlogLogger e così via).

L'oggetto consumatore riceve solo qualcosa che si comporta come un Logger . Non ha bisogno di sapere quale tipo di registratore sta usando - anche se è NullLogger .

III) Un Proxy potrebbe essere qualsiasi oggetto che imita un altro oggetto. Un caso comune è oggetti fittizi . Si avvolge una funzionalità con un'altra.

Quale vuoi implementare dipende dalla tua intenzione:

  • Se stai progettando un oggetto che ha bisogno in qualche modo di utilizzare un'API comune per HttpStackExchangeClient e UdpStackExchangeClient lo si implementerà tramite IStackExchangeClient e un fabbrica

  • Diciamo HttpStackExchangeClient e UdpStackExchangeClient hanno un'API diversa e vuoi che si comporti come l'altro, useresti un proxy che racchiude l'altro.

  • Se hai IStackExchangeClient come interfaccia e HttpStackExchangeClient e UdpStackExchangeClient lo implementano, usare un proxy non ha senso, poiché l'oggetto consumatore non sa quale sta per usare.

I've thought hard about it and I can't choose between one style or the other, but clearly some parts of the .NET framework use factories, and some parts don't, even when there are multiple underlying implementations of its interface.

Nota che il framework ha una cronologia e non è stato sempre progettato a fondo (punterei), il che potrebbe essere il motivo di alcune incongruenze.

    
risposta data 22.09.2015 - 20:24
fonte
0

Usa il semplice costruttore.

In questo caso particolare, la fabbrica è un'API più oscura senza motivo.

Aggiungi la fabbrica quando è chiaramente necessaria.

    
risposta data 26.09.2015 - 09:26
fonte

Leggi altre domande sui tag