2010-08-03 8 views
6

Esto es similar a una de mis otras preguntas, pero lo suficientemente diferente como para justificar una nueva pregunta.Pregunta sobre fábricas abstractas e inyección

Básicamente estoy escribiendo una interfaz de usuario, y mi interfaz de usuario tiene nodos que se pueden seleccionar. Cuando se selecciona un nodo, la interfaz de usuario termina con una clase base de nodo abstracta "INode". A partir de esto obtengo una fábrica haciendo node-> getFactory(), y a partir de esto puedo crear los cuadros de diálogo o vistas apropiados para ese nodo porque el nodo concreto devuelve la fábrica correcta (por ejemplo, factory-> createAddDialog(), factory- > createView (nodo), etc.).

Mi pregunta es acerca de tratar de encontrar la mejor manera para que esa fábrica llegue al nodo en primer lugar.

Hasta el momento he pensado en 3 formas:

1) Inyectar la fábrica correcta cuando se crea el nodo:

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

Así la definición de AreaNode es:

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

    INodeAbstractFactory* getFactory() 
    { 
     return m_injectedFactory; 
    } 

    INodeAbstractFactory* m_injectedFactory; 
}; 

2) Inyecte una fábrica más general y permita que el nodo obtenga la fábrica de esa fábrica:

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

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

    IFactory* m_injectedFactory; 
} 

3) Basta con crear la fábrica de hormigón (aunque esto elimina la posibilidad de utilizar diferentes fábricas para el mismo nodo quizá para pruebas o para los cambios posteriores):

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

pensamientos actuales acerca de estas opciones:

Opción 1: podría ser un poco fortuito. Tendría que asegurarme de que siempre le daría la fábrica correcta para ese tipo, o tal vez podría usar otra fábrica para inyectarme la fábrica correcta.

Opción 2: Obliga al nodo a conocer la implementación abstracta de fábrica lo suficiente como para poder llamar a getAreaNodeFactory, lo que puede no ser tan malo. Al menos ayuda a garantizar que siempre se obtenga la fábrica correcta/misma (suponiendo que la fábrica más general se implemente correctamente).

Opción 3: Esto se siente poco restrictivo ya que no podré cambiar la clase, y no estoy interesado en que el nodo tenga que saber sobre la implementación concreta de la fábrica, aunque en este caso podría ser no sea un problema (¡últimas palabras famosas!).

¿Alguna idea de esto?

Gracias.

EDITAR: Lo sentimos, omitimos las declaraciones de variables en la publicación de origen, corregidas.

EDIT: Otro problema con la opción 2 es que tengo que implementar "getFactory" en cada tipo de nodo. Al menos con la opción 1, la clase base puede simplemente devolver la clase de fábrica abstracta de inyección cada vez ...

+0

no estoy siquiera seguro de los nodos deben hacer referencia a las fábricas. La interfaz de usuario debe hacer referencia a una fábrica abstracta, y debe usar polimorfismo para asegurarse de que se utiliza la fábrica de concreto correcta, según el tipo de nodo que se pase a los métodos de creación. Aparte de eso, supongo que la opción 1 se ve bien. Pero sigo convencido de que los nodos no deben saber acerca de, o tener una referencia a cualquiera de las fábricas. – Kurt

+0

@Kurt: en ausencia de doble despacho, y evitando el uso de una clase de visitantes para cada "acción" interfaz de usuario Me gustaría que el llevar a cabo en el nodo en función del tipo del nodo, lo que podría ser una solución mejor? El tipo de acción que me gustaría realizar en un nodo es a lo largo de las líneas de: createAddDialog (parentWidget), createView (parentWidget), createMiniView (parentWidget), etc. tan sólo pudiera poner los métodos dentro de las clases concretas de nodo, es decir areaNode -> createView (parent) - pero eso no hace mucho por la separación. – Mark

+0

A pesar de que estoy tratando de evitar el uso masivo del patrón de visitante, la única manera que se puede ver por completo para mantener el nodo de interfaz de usuario del lado de las cosas, es el uso de un solo visitante a resolver simplemente la clase de fábrica apropiada, y desde entonces puedo crear mis componentes de interfaz de usuario. – Mark

Respuesta

2

¿Qué tal

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); } 
}; 

argumento deducción plantilla se asegurará de que el tipo de fábrica se deriva de this. Esto debería evitar errores manuales allí. Pero en general, no me molestaría en exponer públicamente una fábrica. Sólo tiene que añadir los siguientes bits de la clase base:

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

Estaba tratando de evitar que los nodos tuvieran tales métodos, simplemente parecía un poco incorrecto que los nodos "supieran" acerca de métodos como "createView", pero tal vez estoy equivocado. – Mark

+0

Los tipos de nodos individuales no. Simplemente hacen referencia a las fábricas. Los detalles de API están restringidos a la clase base, y allí existe para pasar 'this' a los métodos de fábrica. – MSalters

+0

Está bien, veo lo que quieres decir; por otro lado, está el comentario de Kurt que dice que los nodos no deberían hacer referencia a ninguna fábrica, así que nuevamente estoy desgarrado. Inicialmente, utilicé visitantes para hacer doble despacho, pero se volvió demasiado engorroso para usarlos en cada acción, y se hicieron muy grandes debido a la cantidad de tipos de nodo. – Mark

2

Qué tal.

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 dependiendo de su contexto.

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

buena idea, aunque, ¿qué piensas de la idea de que los nodos siquiera conocen fábricas? Kurt arriba piensa que es una mala idea, sin embargo, no puedo pensar en una forma ordenada de nodos sabiendo al menos * algo * sin recurrir al patrón de visitante. – Mark

+0

Ninguno de esos ejemplos realmente funciona. En el primer caso, terminas con un tipo de AreaNode separado para cada Factory; el tipo Factory debe ser interno y no necesita estar expuesto por el tipo de clase. En el segundo caso, ninguno de los instantes de Nodo comparte una clase base común. –

+0

El primer ejemplo podría mejorarse haciendo que el constructor se modele solo, es la única parte de la clase que necesita conocer el tipo de fábrica. –

1

depende de la finalidad del "nodo" Objetos

Si una fábrica debe ser inyectada en un nodo y el nodo concreto no tiene por qué hacer cualquier cosa con el tipo concreto de la fábrica, entonces todo el código relacionado con la fábrica se puede mover a la clase de nodo base simplificando todo.

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; 
}; 

Ahora, la funcionalidad de los nodos de hormigón y fábricas de hormigón se hace ortogonales entre sí y se puede combinar libremente en cualquier manera.

Si un tipo concreto de nodo se enlaza a un tipo concreto de fábrica, entonces puede ser que usted debe evitar fábricas en todo y crear vistas directamente con método de nodos :: createView. Depende de si las fábricas se utilizan en algún otro contexto.

Considera también presente (de manera poco POO-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(); 
    ... 
} 

Tal vez, se adapta a todas sus necesidades)