Cercando di identificare il modello IClass, Class, ClassImpl

5

Recentemente ho visto molto codice che assomiglia a questo:

public interface IFoo { int Bar(); }

public static class Foo 
{
    public static IFoo Create() { return new FooImpl(); }
    private class FooImpl : IFoo
    {
        public int Bar () { ... }
    }
}

In sostanza, esiste un contratto definito da un'interfaccia, la classe dell'oggetto business principale è una classe statica che fornisce i metodi factory e c'è una classe embedded che fornisce l'implementazione effettiva.

Ci sono alcuni vantaggi in questo modello, in particolare, la netta separazione tra il contratto e l'implementazione. Tuttavia, mi sembra un po 'brutto: in particolare il nome di una classe di fabbrica statica con un nome di oggetto business mi sfiora nel modo sbagliato.

Le mie domande sono ...

  1. È un modello o uno stile di sviluppo stabilito?
  2. Ci sono altri vantaggi oltre a una rigorosa copertura dell'implementazione?
  3. Esiste un modo migliore per raggiungere gli stessi obiettivi?
posta afeygin 27.09.2011 - 16:22
fonte

2 risposte

11

Sembra un incrocio storpiato tra Fabbrica astratta e Metodo di fabbrica . Include una classe completa, non solo un metodo, ma quella classe è una classe di utilità statica, non astratta, quindi non è possibile passare a un'implementazione Foo diversa.

Nascondere l'implementazione di Foo va bene, tuttavia questo design non è unità testabile . Molto probabilmente i client chiamano direttamente Foo.Create , quindi i test di unità hanno difficoltà a sostituire / deridere FooImpl a qualsiasi implementazione diversa di IFoo .

Sono d'accordo con te sul fatto che anche la denominazione è cattiva. La fabbrica non deve essere mescolata con il prodotto che crea.

Un'alternativa potrebbe naturalmente essere una completa implementazione di Abstract Factory, ma questo potrebbe essere eccessivo per un singolo prodotto. Molto spesso questo può essere sostituito da Iniezione di dipendenza , che è più semplice e facile da testare.

    
risposta data 27.09.2011 - 16:26
fonte
0

Ritengo che l'approccio sia in molte circostanze il migliore possibile in un framework che non consente alle interfacce di includere metodi di supporto statici pubblici. Considererei IFoo.Create() preferibile a Foo.Create() se tale cosa fosse consentita, ma sfortunatamente (almeno nei linguaggi .NET) non lo è.

Nonostante la sfortunata denominazione del metodo factory, ritengo che un simile approccio sia ottimale nei casi in cui:

  1. Esiste una singola classe che sarà ragionevolmente adatta alle esigenze del codice che non avrà bisogno di utilizzare alcuna funzione oltre a quelle implementate dall'interfaccia.
  2. Alcuni client potrebbero aver bisogno di cose che possono essere utilizzate come implementazioni dell'interfaccia ma che supportano anche altre capacità, e la progettazione interna ottimale di un'implementazione che supporta le funzionalità aggiunte potrebbe essere totalmente diversa dalla progettazione ottimale per un'implementazione che non lo fa.
  3. I metodi che possono essere utilizzati con le implementazioni di base dell'interfaccia dovrebbero anche essere utilizzabili con le implementazioni più elaborate.

Se un metodo accetta un parametro del tipo "classe di implementazione ordinaria", tale metodo non sarà utile con nessuna delle implementazioni più elaborate. Pertanto, a meno che non vi sia un motivo per rifiutare un metodo delle implementazioni più elaborate, i metodi dovrebbero generalmente utilizzare il tipo di interfaccia. Se una variabile non verrà mai passata a nessun metodo tranne che come un tipo di interfaccia, la variabile stessa dovrebbe probabilmente essere dichiarata come lo stesso tipo di interfaccia.

Sebbene in situazioni che coinvolgono un costruttore / fabbrica non parametrico potrebbe non essere molto vantaggioso utilizzare IWidget foo = Widget.Create() su IWidget foo = new Widget() , un metodo factory che accetta parametri può variare il tipo dell'oggetto creato in base ai valori dei parametri a cui è assegnato . Questo può essere particolarmente utile nei casi che riguardano tipi immutabili. Ad esempio, un metodo ImmutableSequence<T>.Create(IEnumerable<T> source); potrebbe copiare source in un array e incapsulare quello in un wrapper immutabile nei casi in cui nessun comportamento alternativo ovviamente migliore suggerisce se stesso, ma nei casi in cui riconosce il tipo di origine che potrebbe essere in grado di fare qualcosa di meglio. Ad esempio, se l'istanza source implementa un metodo AsImmutableSnapshot() , il metodo Create potrebbe eseguire il chaining a tale ed evitare di dover creare un ulteriore livello di nidificazione.

    
risposta data 09.12.2014 - 22:14
fonte

Leggi altre domande sui tag