Tengo una interfaz de usuario con una vista de árbol a la izquierda y un visor a la derecha (un poco como un cliente de correo electrónico). El visor de la derecha muestra el detalle de lo que he seleccionado en el árbol de la izquierda.La mejor manera de implementar acciones en los nodos de árboles, preferiblemente sin usar visitantes
La interfaz de usuario tiene los botones "agregar", "editar" y "eliminar". Estos botones actúan de manera diferente dependiendo de qué "nodo" en el árbol esté seleccionado.
Si selecciono un nodo de un tipo particular, y el usuario hace clic en "editar", entonces necesito abrir el cuadro de diálogo de edición correspondiente para ese tipo particular de nodo, con los detalles de ese nodo.
Ahora, hay muchos tipos diferentes de nodos y la implementación de una clase de visitante se siente un poco desordenada (actualmente mi visitante tiene alrededor de 48 entradas ...). Aunque funciona bien, básicamente para editar, tener algo así como una clase OpenEditDialog que hereda al visitante, y abre el diálogo de edición apropiado:
abstractTreeNode-> accept (OpenEditDialog());
El problema es que tengo que implementar la clase de visitante abstracta para cada "acción" que deseo realizar en el nodo y por alguna razón no puedo evitar pensar que me estoy perdiendo un truco.
La otra manera podría haber sido para implementar las funciones de los nodos a sí mismos:
abstractTreeNode->openEditDialog();
estoy ording el nodo en torno a un poco aquí, así que quizás es mejor:
abstractTreeNode->editClickedEvent();
No puedo evitar pensar que esto está contaminando el nodo.
Pensé en una tercera forma en la que todavía no había pensado tanto. Podría tener una clase de contenedor de plantillas que se agregue al árbol, lo que me permite quizás llamar a funciones libres para realizar cualquier acción, así que supongo que actúa como un intermediario entre nodos e interfaz:
(pseudocódigo) de la parte superior de mi cabeza sólo para dar una idea):
template <class T>
TreeNode(T &modelNode)
{
m_modelNode = modelNode;
}
template <>
void TreeNode<AreaNode>::editClickedEvent()
{
openEditDialog(m_modelNode); // Called with concrete AreaNode
}
template <>
void TreeNode<LocationNode>::editClickedEvent()
{
openEditDialog(m_modelNode); // Called with concrete LocationNode
}
etc ..
Así que esto está extendiendo de manera efectiva los nodos, pero de una manera diferente a la utilización de los visitantes y parece un poco más ordenado bits .
Ahora, antes de seguir adelante y dar el paso utilizando uno de estos métodos, pensé que sería conveniente obtener algo de información.
Gracias! Espero que todo esto tiene algún sentido ..
EDIT:
he burlado de la idea de plantilla envoltorio ..
class INode
{
public:
virtual ~INode() {}
virtual void foo() = 0;
};
class AreaNode : public INode
{
public:
AreaNode() {}
virtual ~AreaNode() {}
void foo() { printf("AreaNode::foo\r\n"); }
};
class RoleNode : public INode
{
public:
RoleNode() {}
virtual ~RoleNode() {}
void foo() { printf("RoleNode::foo\r\n"); }
};
class ITreeNode
{
public:
virtual ~ITreeNode() {}
virtual void bar() = 0;
virtual void foo() = 0;
};
template <class T>
class MainViewTreeNode : public ITreeNode
{
public:
MainViewTreeNode() : m_node() {}
virtual ~MainViewTreeNode() {}
void bar() {}
void foo() { m_node.foo(); }
protected:
T m_node;
};
template <>
void MainViewTreeNode<AreaNode>::bar()
{
printf("MainViewTreeNode<AreaNode>::bar\r\n");
}
template <>
void MainViewTreeNode<RoleNode>::bar()
{
printf("MainViewTreeNode<RoleNode>::bar\r\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
MainViewTreeNode<RoleNode> role;
MainViewTreeNode<AreaNode> area;
std::list<ITreeNode*> nodes;
nodes.push_back(&role);
nodes.push_back(&area);
std::list<ITreeNode*>::iterator it = nodes.begin();
for (; it != nodes.end(); ++it)
{
(*it)->foo();
(*it)->bar();
}
getchar();
return 0;
}
Gracias.
Incluso con algunas operaciones, el patrón de visitante aún podría mejorar el desacoplamiento y la modularidad del código. – Thomas
para que quede claro, ¿quiere decir que quizás en este caso simplemente implemente las funciones directamente en las clases de nodo? – Mark
@marksim: Sí, eso es lo que quise decir. OTOH, @Thomas tiene un punto: está entrelazando operaciones en el árbol con los datos del árbol.Poner datos y operaciones en objetos es de lo que se trata OOP, pero no creo que OOP sea el santo grial. Tiene sus desventajas. Al final, usted es el único de nosotros que conoce lo suficiente del dominio de la aplicación para poder tomar una decisión fundada. Solo podemos insinuar posibles soluciones. – sbi