Una coppia di due classi strettamente accoppiate è meglio di una singola classe più grande?

7

Sto riscrivendo il codice di qualcun altro al minuto, e mi sono imbattuto in due classi che si riferiscono direttamente l'un l'altro e chiamano metodi l'uno sull'altro. Mi piace così (esempio in C #):

class A {
  B otherClass;
  public A() {
    this.otherClass = new B(this);
  }
  // Other methods here call methods on B
}

class B {
  A otherClass;
  public B(A otherClass) {
    this.otherClass = otherClass;
  }
  // Other methods here call methods on A
}

Leggendo il suo codice e cercando di capirlo, devo spostarmi tra le due classi mentre si chiamano.

Queste classi non sono riutilizzabili in modo indipendente senza utilizzare l'altro. Sono consapevole che è considerata una buona pratica evitare i riferimenti circolari (e non è consentito tra i progetti C # in Visual Studio), ma questo, o potrebbe essere, nella tua esperienza è meglio che avere una singola - più grande - classe?

    
posta George Powell 10.02.2013 - 16:54
fonte

7 risposte

4

Di solito non si tratta di due classi indipendenti come moduli logici, ma della loro vita essere indipendenti. Un esempio eccellente è se A è un LinkedList e B è un nodo. Concettualmente, sono solo una cosa, una LinkedList, ma B deve essere separato in modo che possa essere creato e distrutto indipendentemente.

Se ciò non si applica, potresti prendere in considerazione la possibilità di creare una singola classe più grande.

    
risposta data 10.02.2013 - 17:18
fonte
3

Le persone raramente dividono le classi senza una buona ragione. Dove hanno problemi è nei dettagli di come. Prima di ricombinare le classi, prova a riassegnare i metodi tra le classi e prendere in considerazione l'introduzione di una terza classe.

Se un metodo chiama in un'altra classe, prova a spostare il metodo su quell'altra classe. Questo è un po 'come convertire una spinta in un tiro. Compie lo stesso compito, solo il contrario. Questo esercizio a volte può ridurre notevolmente l'accoppiamento.

L'altra cosa che spesso aiuta è l'introduzione di una terza classe. Questo può essere nel livello superiore, contenente istanze di entrambi gli oggetti e arbitraggio tra di loro, nel livello sottostante, come un oggetto POD per condividere dati, o qualche tipo di oggetto risultato, simile a un cursore del database. Un sacco di persone trascurano questo approccio perché sembra che invece di due classi strettamente accoppiate ora ne possiedi tre, ma questo schema è spesso strumentale nel rendere le dipendenze fluide solo in un modo.

    
risposta data 10.02.2013 - 19:08
fonte
1

Anche se probabilmente non è la migliore configurazione, potrebbero esserci alcuni vantaggi per una coppia di classi strettamente accoppiate su una grande classe.

Durante il lavoro con Google Web Toolkit in Java, ho eseguito questo pattern spesso come parte del pattern MVP. La classe A è la vista e la classe B è il presentatore. La vista ha dipendenze dal DOM del browser, rendendo difficile il test, mentre il presentatore contiene tutta la logica. Inserendo una versione beffeggiata della vista nel presentatore, l'applicazione diventa molto più controllabile.

Se le classi non sono state divise per testabilità tramite derisione, allora direi che questa divisione, sebbene forse un passo nella giusta direzione, non è del tutto utile. Certo, può aiutare a separare il codice con quello che fa (come se la classe A gestisse le connessioni del database mentre B stava gestendo le query), ma il codice potrebbe essere riprogettato per non avere la dipendenza circolare.

    
risposta data 10.02.2013 - 17:24
fonte
1

A A e B hanno preoccupazioni chiaramente separate? Se la risposta è sì, è buona quando vengono mantenute come due classi separate. E se si desidera evitare un riferimento circolare, ad esempio, per creare unit test per il test A e B separatamente, definire le interfacce per A, B o entrambi e non chiamare i metodi di A in B direttamente, solo utilizzando l'interfaccia.

    
risposta data 10.02.2013 - 21:30
fonte
0

Come hai descritto le lezioni, penso che vada bene. Non è davvero un riferimento circolare. A dipende da un'istanza B & B non può esistere senza un'istanza di A. Non puoi creare un'istanza di B con un A.

L'unica cosa che guarderei è se la funzionalità all'interno di ogni classe è appropriata.

cioè pensa a ciascuno come una scatola nera. Potresti creare una nuova classe derivata da B e usarla in A? Potresti farlo senza sapere cosa stava facendo A? Potresti fare lo stesso con A senza sapere cosa stava facendo B?

    
risposta data 10.02.2013 - 19:00
fonte
0

Le classi A e B potrebbero entrambe derivare da altre classi, cosa che non sarebbe possibile con una singola classe, poiché C # non ha ereditarietà multipla.

A volte questo tipo di dipendenza reciproca sembra del tutto naturale. Pensa a una relazione auto / autista. Non sarebbe naturale combinare entrambi in una classe.

Sarebbe comunque meglio non rendere entrambe le classi direttamente dipendenti l'una dall'altra, ma fargli conoscere l'una con l'altra attraverso le interfacce. Ciò migliorerebbe la modularità e ti consentirebbe di fornire un'altra implementazione per una classe, lasciando l'altra intatta.

    
risposta data 10.02.2013 - 19:21
fonte
0

Quando hai due classi che saltano avanti e indietro tra loro, potrebbe significare che non hai colpito correttamente le astrazioni. Combinare quel codice in una classe sembra spezzare la Single Responsibility (se non è già rotta) quando si tengono presenti i principi SOLID. Quando cerco di risolvere il problema con il refactoring del codice legacy, ciò che sembra funzionare meglio è sottoporre ogni classe a test. I test unitari ti aiuteranno a rivelare ciò che ogni classe sta cercando di realizzare.

    
risposta data 10.02.2013 - 21:56
fonte

Leggi altre domande sui tag