Nella mia esperienza, è utile separare le operazioni di basso livello dall'API pubblica della classe.
L'API pubblica ha nomi di metodi convenienti e fornisce l'incapsulamento. Ad esempio, è possibile consentire l'iterazione e la ricerca di chiavi, ma impedire l'accesso diretto alla struttura e al layout dell'albero. I client dovrebbero essere in grado di utilizzare la struttura dati senza sapere che è implementata come un albero AVL. I test unitari dell'API verificano che l'albero funzioni come previsto, senza accertarsi che le operazioni specifiche dell'AVL funzionino correttamente.
Il livello di basso livello implementa le operazioni specifiche di AVL. La maggior parte dei client non dovrebbe utilizzare direttamente questo livello, ma solo tramite l'API pubblica. Questo livello non fornisce alcun incapsulamento della struttura AVL, quindi è necessario utilizzarlo correttamente. Va bene se questo livello usa la programmazione procedurale invece del design OOP. Sebbene questo layer non venga utilizzato da altri client, dovrebbe essere pubblico in modo che i test unitari possano verificare l'implementazione. I test unitari a questo livello possono far valere dettagli sulla struttura ad albero, ad es. che le rotazioni funzionino come previsto.
Si noti che l'API pubblica non deve ereditare dal livello inferiore: si desidera solo utilizzare l'implementazione, non ereditare l'interfaccia.
Progetto di esempio:
// implementation details
public class Node<K, V> {
...
}
public class AVLDetails {
public static <K, V> void insert(Node<K, V> root, Node<K, V> n) { ... }
public static <K, V> void rotateRight(Node<K, V> n) { ... }
public static <K, V> void rotateLeft(Node<K, V> n) { ... }
...
}
// public API
public class Tree<K, V> {
private Node<K, V> root;
...
public void put(K key, V value) {
AVLDetails.insert(root, new Node<>(key, value));
}
}