Perché supportare le dipendenze cicliche? Ci sono casi d'uso validi?

3

Sfondo

Tempo fa ho imparato qualcosa sul design del pacchetto , in particolare su accoppiamento lento :

The Acyclic Dependencies Principle

The dependency structure between packages must be a Directed Acyclic Graph (DAG). That is, there must be no cycles in the dependency structure.

Robert Cecil Martin aka uncle Bob, Granularity

Le domande

  1. Perché ci sono sistemi come nodejs (dove lavoro di più, ma la domanda potrebbe riguardare altri) che supportano le dipendenze cicliche tra i moduli? Per chiarire: all'interno di nodejs, un module può essere un file di codice sorgente o una cartella contenente un pacchetto .

  2. Esistono casi d'uso reali considerati validi? Per favore, non essere "esempi di cattiva progettazione" (spiegato sotto)

Esempi di cattiva progettazione

La domanda (2) si riferisce ad esempi in cui ci sono dipendenze cicliche che sarebbero meglio rifattorizzate per evitarle, risultando in un design migliore.

Come esempio di ciò che intendo con la precedente affermazione, vedi questa risposta di Doc Brown dove espone il "file system example", ma si prega di notare questa domanda non riguarda le dipendenze cicliche tra Classi .

Domande correlate ai programmatori SE

posta laconbass 30.04.2016 - 18:55
fonte

1 risposta

2

Le dipendenze dei moduli ciclici spesso si verificano quando si ha un modulo che funge da interfaccia tra il codice utente e (diversi) moduli di implementazione.

Tipicamente, un tale modulo di interfaccia definisce tipi e funzionalità generiche per i moduli di implementazione da utilizzare, motivo per cui i moduli di implementazione dipendono dal modulo di interfaccia. Tuttavia, per fornire una vera astrazione al codice utente (= evitare le dipendenze del codice utente sui moduli di implementazione), il modulo di interfaccia deve interagire con i diversi moduli di implementazione e quindi dipendere da essi.

Se si sostituisce "module" con "class", questo diventa molto più chiaro: il modulo di interfaccia è una classe astratta che fornisce l'intera interfaccia utente. Le diverse implementazioni ereditano dalla classe astratta e quindi dipendono dall'interfaccia astratta. Tuttavia, per rendere invisibile all'utente l'esistenza stessa delle sottoclassi, la classe astratta deve anche fornire metodi di fabbrica che selezionino le diverse implementazioni sotto il cofano. Poiché le fabbriche hanno bisogno di instanziare le classi concrete, dipendono da loro.

Naturalmente, una tale architettura di classe verrebbe solitamente incapsulata all'interno di un singolo modulo, quindi non si avranno ancora le dipendenze dei moduli circolari. Tuttavia, la stessa struttura può essere applicata al codice in un contesto più ampio, portando a una situazione in cui si avrebbero reali dipendenze dei moduli ciclici.

Di solito, tali dipendenze cicliche possono essere suddivise dividendo l'interfaccia astratta in due parti, una da cui dipendono i moduli di implementazione e una che dipende dai moduli di implementazione. Questo di solito ha i lati negativi di 1. un modulo è troppo piccolo per giustificare che sia un modulo, e 2. aggiunge complessità all'interfaccia pubblica poiché il codice utente ora deve dipendere direttamente da due moduli.

Un altro approccio per rompere le dipendenze cicliche sarebbe che i moduli di implementazione si registrassero con il modulo di interfaccia. Anche questo ha due svantaggi: 1. Impone una singola interfaccia di callback nei moduli di implementazione, che potrebbe non essere appropriata, e 2. aggiunge in modo significativo la complessità per la registrazione e la gestione delle implementazioni registrate all'interno del modulo di interfaccia.

Quindi, sì, ci sono situazioni valide in cui la restrizione alle dipendenze dei moduli non-circolari porta a un codice più complesso, rendendo l'uso di un ciclo di dipendenze l'alternativa preferibile. E i buoni linguaggi di programmazione lo consentono.

    
risposta data 03.05.2016 - 21:00
fonte

Leggi altre domande sui tag