Quasi ogni progetto che ha un modello o documento modificabile avrà una struttura gerarchica. Può essere utile implementare il "nodo gerarchico" come classe base per entità diverse. Spesso l'elenco collegato (fratello minore, 2 ° modello) è il modo naturale in cui crescono molte librerie di classi, tuttavia i bambini possono essere di diversi tipi e probabilmente un " modello a oggetti " non è ciò che consideriamo quando parliamo di alberi in generale.
La mia implementazione preferita di un albero (nodo) del tuo primo modello è un one-liner (in C #):
public class node : List<node> { /* props go here */ }
Eredita da un elenco generico del tuo tipo (o eredita da qualsiasi altra raccolta generica del tuo tipo). Camminare è possibile in una direzione: forma la radice verso il basso (gli oggetti non conoscono i loro genitori).
Albero solo genitore
Un altro modello che non hai menzionato è quello in cui ogni bambino ha un riferimento al suo genitore:
null
|
+---------+---------------------------------+
| parent |
| root |
+-------------------------------------------+
| | |
+---------+------+ +-------+--------+ +--+-------------+
| parent | | parent | | parent |
| node 1 | | node 2 | | node 3 |
+----------------+ +----------------+ +----------------+
Camminare su questo albero è possibile solo viceversa, normalmente tutti questi nodi saranno archiviati in una raccolta (array, tabella hash, dizionario ecc.) E un nodo sarà localizzato cercando la raccolta su criteri diversi dalla posizione gerarchica nella albero che in genere non sarebbe di primaria importanza.
Questi alberi solo per i genitori sono di solito visualizzati nelle applicazioni di database. È abbastanza facile trovare i figli di un nodo con le istruzioni "SELECT * WHERE ParentId = x". Tuttavia raramente li troviamo trasformati in oggetti di classe ad albero-nodo in quanto tali. Nelle applicazioni statefull (desktop) possono essere racchiuse in controlli di nodi dell'albero esistenti. Nelle applicazioni senza stato (web) anche questo può essere improbabile. Ho visto gli strumenti del generatore di classi di mappatura ORM generare errori di overflow dello stack durante la generazione di classi per le tabelle che hanno una relazione con se stessi (ridacchiare), quindi forse questi alberi non sono poi così comuni.
alberi navigabili bidirezionali
Nella maggior parte dei casi pratici, tuttavia, è conveniente avere il meglio di entrambi i mondi. Nodi che hanno un elenco di bambini e inoltre conoscono il loro genitore: alberi navigabili bidirezionali.
null
|
+--------------------+--------------------+
| parent |
| root |
| child1 child2 child3 |
+--+------------------+----------------+--+
| | |
+---------+-----+ +-------+-------+ +---+-----------+
| parent | | parent | | parent |
| node1 | | node2 | | node3 |
| child1 child2 | | child1 child2 | | child1 child2 |
+--+---------+--+ +--+---------+--+ +--+---------+--+
| | | | | |
Ciò comporta molti altri aspetti da considerare:
- Dove implementare il collegamento e lo scollegamento dei genitori?
- lascia che la logica di affari si prenda cura di te e lascia l'aspetto fuori dal nodo (dimenticheranno!)
- i nodi hanno metodi per creare figli (non consente il riordino) (scelta di Microsofts nella loro implementazione DOM System.Xml.XmlDocument, che mi ha fatto quasi impazzire quando l'ho incontrato per la prima volta)
- I nodi accettano un genitore nel loro costruttore (non consente il riordino)
- in tutti i metodi add (), insert () e remove () e i loro sovraccarichi dei nodi (di solito la mia scelta)
- persistenza
- Come camminare sull'albero durante la persistenza (ad esempio tralasciare i collegamenti principali)
- Come ricostruire il collegamento bidirezionale dopo la deserializzazione (impostando nuovamente tutti i genitori come azione post-deserializzazione)
- notifiche
- Meccanismi statici (flag IsDirty), gestire ricorsivamente nelle proprietà?
- Eventi, passa in rassegna attraverso i genitori, in giù attraverso i bambini o in entrambi i modi (ad esempio considera il pump dei messaggi di Windows).
Ora, per rispondere alla domanda , gli alberi navigabili bidirezionali tendono ad essere (nella mia carriera e campo finora) i più utilizzati. Esempi sono l'implementazione di Microsofts di System.Windows.Forms.Control o System.Web.UI.Control nel framework .Net, ma anche ogni implementazione DOM (Document Object Model) avrà nodi che conoscono il loro genitore e un elenco dei loro figli. Il motivo: facilità d'uso e facilità di implementazione. Inoltre, si tratta generalmente di classi di base per classi più specifiche (XmlNode può essere la base delle classi Tag, Attribute e Text) e queste classi di base sono luoghi naturali in cui inserire architetture generiche di serializzazione e gestione degli eventi.
L'albero è al centro di molte architetture, ed essere in grado di navigare liberamente significa essere in grado di implementare soluzioni più velocemente.