2011-02-02 10 views
17

Il modello di fabbrica C# richiede un upcast?C# factory - l'upcast è un must?

Voglio che Dio nella libreria di classi G crei un Adam nella libreria di classi A senza G dipendente da A. Dio produce Adams per il consumo di Eva nella libreria di classe E, ed è OK per Eva sapere e dipendere da Adam. (modifica - questo esempio continua a migliorare :)

La soluzione che potrei pensare è avere un AdamFactory in A. In questo modo AdamFactory conosce Adam e può facilmente crearlo (possibilmente chiamando semplicemente il costruttore di Adam). Dio riceve un AdamFactory e può ordinarlo a CreateAdam.

Ora, poiché a Dio non è consentito conoscere Adam, il CreateAdam di AdamFacotry deve restituire un oggetto, e ciò richiede ad Eve di eseguire il cast dell'oggetto restituito da AdamFactory ad un Adam.

Questo funzionerà, credo. Tuttavia, mi sento a disagio quando parlo di up-casting visto che è un no-no. È davvero un must?

P.S. - Nessuna intenzione di blasfemia, e mi scuso se i sentimenti di qualcuno sono stati feriti. Sembrava meglio usare Dio e Adamo invece di Creatore e Creato perché le ultime due parole sono troppo simili tra loro.

Modifica: suggerimento Re interfacce. Supponiamo che Adam abbia due metodi: ProvideLove, ProvideFood e ProvideProtection (stiamo mantenendo questo esempio kis-safe :). Eva usa Adam per questi due scopi, ma naturalmente Dio no. Quindi, perché fornire a Dio la consapevolezza che AdamFactor restituisce qualcosa che implementa un IAdam e non solo un oggetto? Non capisco!

Edit: Il codice di lavoro (con tutti nella stessa libreria, che il mio obiettivo è quello di separare a diverse librerie) simile a questa:

Adam God.LoadAdam(AdamID theAdamID) 
     var adam = new Adam(theAdamId, this) 

Adam.Adam(AdamID theAdamID, God theGod) 
     _god = theGod 
     _mind = theGod.LoadMind(theAdamId, this) 

Mind God.LoadMind (AdamID theAdamID, Adam theAdam) 
     var mind = new Mind (theAdam) 
     var mindId = new minId(theAdamId) 
     mind.DeserializeFromFile(minId) 

Mind.Mind (Adam theAdam) 
     _adam = theAdam 
+2

Credo che tu intenda * ridurre * l'oggetto restituito a un Adam. –

+0

Vorrei andare per le interfacce, come indicato nella prima risposta. Ad ogni modo, +1 per una bella domanda, molto divertente :-) – Shimrod

+3

Hai svalutato la domanda per il campione prima ancora di leggerla fino alla fine. :) – Stilgar

risposta

9

Non sono sicuro di aver capito completamente i requisiti, ma ecco un suggerimento:


//in assembly G 
public abstract class HumanFactory<T> 
{ 
    public abstract T CreateHuman(); 
} 

//in assembly A 
public class Adam { } 
//in assembly A 
public class AdamFactory : HumanFactory<Adam> 
{ 
    public override Adam CreateHuman() 
    { 
     return new Adam(); 
    } 
} 

//in assembly G 
public class God 
{ 
    public T Create<T>(HumanFactory<T> factory) 
    { 
     return factory.CreateHuman(); 
    } 
} 

e l'utilizzo:


//Somewhere in assembly E 
Adam adam = new God().Create(new AdamFactory()); 
+0

+1 dato l'intero set "Domanda + commenti" Direi che questa è la soluzione più sensata. Nondimeno, c'è qualcosa di sbagliato nel design di OP, IMHO. – Simone

+0

@Simone questo disegno può avere alcuni usi. Per essere utili ci dovrebbero essere vincoli generici basati su interfacce comuni o classi base per Adam e le altre istanze create da Dio.Allora Dio può fare qualche altro lavoro se non chiamando ciecamente la AdamFactory. Anche la classe base HumanFactory può avere metodi addizionali che Dio può usare per svolgere un lavoro aggiuntivo. – Stilgar

+0

@Simone, Dio può rendere Adamo vivo per ogni ordinando il lato database delle cose ecc., Eva non può farlo perché è fissa in un punto siglato (ma in movimento) sulla linea del tempo. –

1

Cosa succede ad usare le interfacce in modo da Dio sa iAdam, o qualcosa del genere come l'umano?

+0

Dio non deve usare Adam. Quindi, come potrebbe beneficiare Dio dell'interfaccia? – Avi

0

Stai descrivendo un abstract factory pattern, anche se le fabbriche "figlio" (ad esempio AdamFactory) normalmente hanno qualcosa in comune, quindi ti aspetteresti che producano qualcosa di più come un'interfaccia comune piuttosto che un semplice oggetto (vedi la risposta di Davide)

Hai ragione a preoccuparti del ruolo del cast perché legherà Eve all'attuazione di Adam che sconfigge lo scopo di una fabbrica (a meno che tu non lo stia davvero usando come costruttore).

La domanda è: perché hai bisogno della classe di Dio?

0

Beh, se Dio è una libreria di classi di persistenza come lei ha ricordato nel commentare, quindi non dovrebbe influenzare il design della classe del resto del sistema.

Quindi, non aggiungerei interfacce non necessarie. È corretto restituire un oggetto da deserializzatore e downcast in seguito (ad es.BinaryFormatter, XmlSerializer).

Il modo migliore sarebbe rendere generico il tuo deserializzatore.

0

OK, che ne dite di spolverare in alcuni generici qui?

Diciamo che Dio può accettare Fabbriche di qualsiasi tipo T che aderiscono al seguente interfaccia:

interface IFactory { 
    Type CreatedType { get; } 
    object Create(); 
} 

Una classe astratta per implementare questo può apparire come questo:

abstract class AbstractFactory<T> : IFactory { 
    public Type CreatedType { get { return typeof(T); } 
    public virtual object Create() { 
    return innerCreate(); 
    } 
    protected abstract override T innerCreate(); 
} 

Ora, si può registrare fabbriche con Dio:

God.RegisterFactory(new AdamFactory()); 

AdamFactory eredita da AbstractFactory<Adam>

Dio memorizza le sue fabbriche in un dizionario in cui la chiave è il tipo restituito dall'interfaccia iFactory

Crea Metodo ora sembra che:

God.Create<Adam>(); 

Dio guarda nelle sue fabbriche, vede lì è uno per typeof (T) del metodo generico, recupera la fabbrica, chiama crea e downcasts a T.

Cosa ne pensi?

+1

Ma poi stai quasi iniziando a costruire un container IoC - Certo, Dio è un bel nome per un contenitore IoC, ma ce ne sono già un paio in giro .. – flq

+0

Dio forse più di un contenitore di servizi ... – DaeMoohn