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:
- 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.
- 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.
- 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.