Ho un'interfaccia utente con una vista ad albero sulla sinistra e un visualizzatore sulla destra (un po 'come un client di posta elettronica). Il visualizzatore sulla destra mostra i dettagli di tutto ciò che ho selezionato nell'albero a sinistra.Il modo migliore per implementare azioni performanti su nodi ad albero, preferibilmente senza usare visitatori
L'interfaccia utente dispone di pulsanti "aggiungi", "modifica" e "elimina". Questi pulsanti agiscono in modo diverso a seconda del "nodo" nell'albero selezionato.
Se si seleziona un nodo di un particolare tipo e l'utente fa clic su "modifica", è necessario aprire la finestra di dialogo di modifica appropriata per quel particolare tipo di nodo, con i dettagli di tale nodo.
Ora, ci sono molti tipi diversi di nodi e l'implementazione di una classe di visitatori è un po 'caotica (attualmente il mio visitatore ha circa 48 voci ...). Funziona bene però - fondamentalmente per la modifica di avere qualcosa come una classe OpenEditDialog che eredita il visitatore e apre la finestra di modifica appropriata:
abstractTreeNode-> accept (OpenEditDialog());
Il problema è che devo implementare la classe di visitatori astratti per ogni "azione" che voglio eseguire sul nodo e per qualche motivo non posso fare a meno di pensare che mi manca un trucco.
L'altro modo potrebbe essere stata per implementare le funzioni nei nodi stessi:
abstractTreeNode->openEditDialog();
che sto ording il nodo intorno un po 'qui, quindi forse questo è meglio:
abstractTreeNode->editClickedEvent();
Non posso fare a meno di pensare che questo sta inquinando il nodo però.
Ho pensato a un terzo modo che non ho ancora pensato tanto. Potrei avere una classe wrapper basata su modelli che viene aggiunta all'albero che consente di chiamare le funzioni libere per eseguire qualsiasi azione, quindi suppongo che agisca da interfaccia tra i nodi e l'interfaccia:
(codice pseudo la parte superiore della mia testa solo per dare un'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
}
ecc ..
Quindi questo è efficacemente estendendo i nodi, ma in un modo diverso di utilizzare il visitatore e sembra un po 'più ordinato .
Ora prima di andare avanti e fare il grande passo con uno di questi metodi, ho pensato che sarebbe saggio ottenere un input.
Grazie! Spero che tutto questo fa un certo senso ..
EDIT:
ho preso in giro l'idea involucro su modelli ..
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;
}
Grazie.
Anche con poche operazioni, il pattern Visitor potrebbe ancora migliorare il disaccoppiamento e la modularità del codice. – Thomas
solo per essere chiari, vuoi dire forse in questo caso solo implementare le funzioni direttamente nelle classi del nodo? – Mark
@marksim: Sì, questo è quello che intendevo. OTOH, @Thomas ha un punto: si tratta di operazioni intrecciate sull'albero con i dati dell'albero.Mettere dati e operazioni in oggetti è ciò che è OOP, ma non penso che OOP sia il Santo Graal. Ha i suoi svantaggi. Alla fine, sei l'unico di noi che conosce abbastanza il dominio dell'applicazione per essere in grado di prendere una decisione ben fondata. Possiamo solo suggerire possibili soluzioni. – sbi