'Incapsulamento delle prestazioni Vs' in una lista collegata bloccabile

3

Lo scopo principale di incapsulamento è di proteggere le varianti in-varianti di qualsiasi class (in java).

Qui è il codice completo per class DList / class lockDList / class DListNode / class LockDListNode .

class DList e la sottoclasse class LockDList ha le seguenti invarianti.

/* DList invariants:
   *  1)  sentinel != null.
   *  2)  For any DListNode x in a DList, x.next != null.
   *  3)  For any DListNode x in a DList, x.prev != null.
   *  4)  For any DListNode x in a DList, if x.next == y, then y.prev == x.
   *  5)  For any DListNode x in a DList, if x.prev == y, then y.next == x.
   *  6)  size is the number of DListNodes, NOT COUNTING the sentinel,
   *      that can be accessed from the sentinel (sentinel) by a sequence of
   *      "next" references.
   */

Ma l'utente di class LockDList e class DList può passare / ricevere il nodo elenco errato. Ciò porta a rompere l'incapsulamento a causa dell'unica ragione, ovvero in-varianti non protette , perché l'utente può passare il nodo sbagliato a class DList o class LockDList Ad esempio , sotto è il metodo pubblico next in class DList .

public DListNode next(DListNode node) {
      if ((node == null) || (node.getNext() == this.sentinel)){
          return null;
      }else{
          return node.getNext();
      }
  }

La maggior parte del codice di produzione tenta di introdurre un'implementazione O (1), mantenendo item di class DListNode come public .

Esiste una decisione di progettazione che può essere suggerita per mantenere l'incapsulamento senza impatto sulle prestazioni?

    
posta overexchange 10.08.2015 - 06:37
fonte

3 risposte

3

Non esponi ciò che l'utente non deve sapere.

Se l'interfaccia principale è solo DList , non devi esporre DListNode all'utente delle tue classi. Puoi rendere DListNode una classe privata, che è manipolata internamente solo da DList .

Nel caso di iterazione attraverso il tuo elenco, ad es. next() , puoi spostarlo in un enumeratore o iteratore come classe, che contiene un elenco associato. In questo modo puoi nascondere la classe DListNode e l'utente non può in alcun modo passare casualmente i nodi da una lista a un'altra.

Se non hai modo di nascondere tali classi, devi aggiungere una sorta di proprietà in queste classi di implementazione e controllarle, per essere sicuri che non accada alcun incidente.

    
risposta data 10.08.2015 - 10:46
fonte
2

Il tuo problema principale deriva dall'utente che passa un nodo che non appartiene all'elenco, come hai detto nel tuo commento :

[...] if the user passes node of list1 to list2 by saying list2.someMethod(nodeOfList1)

Si potrebbe cercare di evitare di lasciare che l'utente manipoli i nodi, nel qual caso è necessario un modo indiretto per accedervi, ma ciò spinge il problema un ulteriore passo avanti. Quello che devi fare in questo caso è controllare che il nodo appartenga alla lista. Quando questo è vero, allora i tuoi invarianti sono soddisfatti.

È possibile aggiungere un riferimento owner da DListNode a DList e inizializzarlo durante la costruzione: ad esempio, dlist.insertBack(Object) restituirà un DListNode che fa riferimento a dlist . owner non è mutabile e può essere reso pubblico finale (oppure, usa un getter). Quindi, è necessario controllare this == node.owner quando si manipolano i nodi all'interno delle operazioni di lista o si genera un errore di runtime.

    
risposta data 10.08.2015 - 09:42
fonte
1

Puoi aggiungere un attributo myDList alla classe DListNode e implementare un metodo come next nel modo seguente:

public DListNode next(DListNode node) {
   if (node == null)
      return null;
   if(node.getMyDList()!=this)
      throw // ...some exception here
   if (node.getNext() == this.sentinel)
      return null;
   return node.getNext();
}

Questo non aggiungerà una penalità significativa per le prestazioni, ma aggiunge una penalità di memoria, perché ogni nodo deve contenere ora un riferimento extra.

EDIT: puoi anche evitare la penalità della memoria quando segui il suggerimento di Olaf Dietsche. L'introduzione di una classe iteratore eviterà la necessità di una funzione DList.next() , e la corrispondente funzione next dell'iteratore non avrà bisogno di parametri, il che elimina la possibilità di passare qualsiasi valore incoerente.

    
risposta data 10.08.2015 - 09:44
fonte