2010-08-03 23 views
6

Questo è simile a una delle mie altre domande, ma abbastanza diverso penso di giustificare una nuova domanda.Domanda su fabbriche astratte e iniezione

Fondamentalmente sto scrivendo un'interfaccia utente e la mia interfaccia utente ha nodi che possono essere selezionati. Quando viene selezionato un nodo, l'interfaccia utente termina con una classe base di nodo astratta "INode". Da questo ottengo una factory facendo node-> getFactory(), e da questo posso creare le finestre di dialogo o le viste appropriate per quel nodo perché il factory corretto viene restituito dal nodo concreto (ad esempio factory-> createAddDialog(), factory- > createView (nodo), ecc.).

La mia domanda è di provare a trovare il modo migliore per quella fabbrica di entrare nel nodo in primo luogo.

Finora ho pensato di 3 modi:

1) Iniettare la corretta fabbrica quando creo il nodo:

AreaNode *node = new AreaNode(new AreaNodeFactory()); 

Così la definizione di AreaNode è:

AreaNode : public INode 
{ 
    AreaNode(INodeAbstractFactory *injectedFactory) 
    { 
     m_injectedFactory = injectedFactory; 
    } 

    INodeAbstractFactory* getFactory() 
    { 
     return m_injectedFactory; 
    } 

    INodeAbstractFactory* m_injectedFactory; 
}; 

2) Iniettare uno stabilimento più generale e consentire al nodo di ottenere la fabbrica da quella fabbrica:

AreaNode : public INode 
{ 
    AreaNode(IFactory *injectedFactory) 
    { 
     m_injectedFactory = injectedFactory; 
    } 

    INodeAbstractFactory* getFactory() 
    { 
     return m_injectedFactory->getAreaNodeFactory(); 
    } 

    IFactory* m_injectedFactory; 
} 

3) Basta creare la fabbrica di cemento (anche se questo elimina la possibilità di utilizzare diverse fabbriche per lo stesso nodo, forse per il test o per le modifiche successive):

AreaNode : public INode 
{ 
    INodeAbstractFactory* getFactory() 
    { 
     return new AreaNodeFactory(); 
    } 
} 

pensieri attuali su queste opzioni:

Opzione 1: Potrebbe essere un po 'casuale: dovrei assicurarmi di dargli sempre la fabbrica corretta per quel tipo, o forse potrei semplicemente usare un'altra fabbrica per iniettare la fabbrica corretta per me.

Opzione 2: costringe il nodo a conoscere l'implementazione della fabbrica astratta abbastanza da essere in grado di chiamare getAreaNodeFactory, che potrebbe non essere una cosa così brutta. Almeno aiuta a garantire che lo stesso factory sia sempre corretto (presupponendo che lo stabilimento più generale sia implementato correttamente).

Opzione 3: Questo si sente un po 'restrittivo in quanto non sarò in grado di scambiare la lezione, e non sono entusiasta del nodo che deve conoscere l'implementazione concreta della fabbrica - anche se in questo caso potrebbe non essere troppo di un problema (ultime parole famose!).

Qualche idea su questo?

Grazie.

MODIFICA: Scusate le dichiarazioni delle variabili nel post di origine, corrette.

EDIT: Un altro problema con l'opzione 2 è che devo attuare "getFactory" in ogni tipo di nodo. Almeno con l'opzione 1, la classe base può solo restituire ogni volta l'inject abstract factory.

+0

io non sono nemmeno sicuro i nodi devono fare riferimento le fabbriche. L'interfaccia utente dovrebbe fare riferimento a una fabbrica astratta e utilizzare il polimorfismo per assicurarsi che venga utilizzata la fabbrica di calcestruzzo corretta, in base al tipo di nodo passato nei metodi di creazione. A parte questo, suppongo che l'opzione 1 sia ok. Ma rimango convinto che i nodi non dovrebbero sapere o tenere un riferimento a nessuna fabbrica. – Kurt

+0

@Kurt: nell'assenza di doppia spedizione, ed evitando l'uso di una classe di visitatori per ogni "interfaccia" di interfaccia utente mi piacerebbe l'esecuzione sul nodo in base al tipo di nodo, quale potrebbe essere una soluzione migliore? Il tipo di azione che vorrei eseguire su un nodo è sulla falsariga di: createAddDialog (parentWidget), createView (parentWidget), createMiniView (parentWidget), ecc. Potrei semplicemente inserire i metodi nelle classi di nodi concreti, ovvero areaNode -> createView (parent) - ma questo non fa molto per la separazione. – Mark

+0

Anche se sto cercando di evitare l'uso massiccio del pattern visitor, l'unico modo che posso vedere per mantenere completamente il nodo fuori dal lato dell'interfaccia utente è quello di utilizzare un singolo visitatore per risolvere la classe factory appropriata e da quindi posso creare i miei componenti dell'interfaccia utente .. – Mark

risposta

2

Come su

class FactoryBase { /* interface */ } 
template <typename NodeType> class Factory : public FactoryBase { 
    // Default implementation. 
} 
// Add appropriate specializations for all relevant nodetypes, if needed. 
template <typename NodeType> inline Factory<NodeType> getFactory(NodeType*) { 
    return Factory<NodeType>(); 
} 

class AreaNode : public Node { 
    FactoryBase getFactory() { return getFactory(this); } 
}; 

Template argomento deduzione farà in modo che il tipo di fabbrica è derivato da this. Questo dovrebbe impedire errori manuali lì. Ma in generale, non mi preoccuperei di esporre pubblicamente una fabbrica. Basta aggiungere i seguenti bit alla classe di base:

class Node { 
public: 
    std::Auto_ptr<View> createView() { 
    this->getFactory()->createView(this); 
    } // etc. 
private: 
    virtual FactoryBase* getFactory() = 0; 
}; 
+0

Stavo cercando di impedire ai nodi di avere tali metodi - sembrava solo un po 'sbagliato che i nodi "sapessero" su metodi come "createView", ma forse ho torto. – Mark

+0

I singoli tipi di nodo non lo farebbero. Fanno solo riferimento alle fabbriche. I dettagli dell'API sono limitati alla classe base ed esiste per passare 'this' ai metodi factory. – MSalters

+0

Okay capisco cosa intendi - d'altra parte c'è il commento di Kurt sopra che i nodi non dovrebbero avere riferimenti a nessuna fabbrica - quindi di nuovo sono strappato. Inizialmente usavo i visitatori per fare il doppio invio, ma diventava troppo ingombrante per usarli per ogni azione, e diventavano molto grandi a causa del numero di tipi di nodi. – Mark

2

Che ne dici?

template<typename Factory> 
class AreaNode : public INode 
{ 
public: 
     virtual ~AreaNode(){} 

     AreaNode() : pFactory_(new Factory()) 
     {   
     } 

     const shared_ptr<IFactory>& GetFactory() 
     { 
      return pFactory_; 
     } 
private:   
     shared_ptr<IFactory> pFactory_; 
}; 

EDIT:

O a seconda del contesto.

template<typename Factory> 
class Node 
{ 
public: 
     virtual ~Node(){} 

     Node() : pFactory_(new Factory()) 
     {   
     } 

     const shared_ptr<IFactory>& GetFactory() 
     { 
      return pFactory_; 
     } 
private:   
     shared_ptr<IFactory> pFactory_; 
}; 

class AreaNode : public Node<AreaNodeFactory> 
{ 
    // Implementation 
}; 

// OR 

typedef Node<AreaNodeFactory> AreaNode; 
+0

bella idea - anche se cosa ne pensi dell'intera idea dei nodi, anche conoscendo le fabbriche? Kurt sopra pensa che sia una cattiva idea, tuttavia non riesco a pensare a un modo pulito intorno ai nodi di sapere almeno * qualcosa * senza ricorrere al modello di visitatore. – Mark

+0

Nessuno di questi esempi funziona davvero. Nel primo caso si finisce con un tipo di AreaNodo separato per ogni Factory: il tipo Factory deve essere interno e non deve essere esposto dal tipo della classe. Nel secondo caso nessuna istanza del nodo condivide una classe base comune. –

+0

Il primo esempio potrebbe essere migliorato solo rendendo il costruttore del template: è l'unica parte della classe che deve conoscere il tipo di fabbrica. –

1

dipende dallo scopo del "nodo" oggetti

Se una fabbrica dovrebbe essere iniettato in un nodo e nodo di cemento non devono fare qualsiasi cosa con il tipo concreto della fabbrica, quindi tutto il codice relativo alla fabbrica può essere spostato nella classe del nodo di base, semplificando tutto.

class INode //well, it's not "interface" in clear sense 
{ 
public: 
    void setFactory(Factory* f) { this->f = f; } 
    Factory* getFactory() const { return f; } 
private: 
    Factory* f; 
}; 

Ora, la funzionalità dei nodi calcestruzzo e fabbriche di calcestruzzo è fatto ortogonali tra loro e può essere liberamente combinati in qualsiasi modo.

Se un tipo concreto di nodo è associato a un tipo concreto di fabbrica, potrebbe essere opportuno evitare le fabbriche e creare viste direttamente con il metodo node :: createView. Dipende dal fatto che le fabbriche siano utilizzate in un altro contesto.

Guarda anche questo (modo non molto OOP-ish):

typedef boost::function0<Factory*> Node; 
std::map<UIItem*,Node> nodes; 
nodes[area_item1] = &getAreaFactory; 
nodes[area_item2] = &getAreaFactory; 
nodes[region_item1] = &getRegionFactory; 
... 
void OnSelect(UIItem* i) 
{ 
    ... 
    View* v = nodes[i]()->createView(); 
    ... 
} 

Forse, si adatta tutte le vostre esigenze)