Quanto segue rigorosamente la regola "No Dependency Cycle" (NDepend)

10

Un po 'di background: come guida di una squadra, uso NDepend una volta alla settimana per verificare la qualità del nostro codice. Soprattutto la copertura del test, le linee di codice e le metriche di complessità ciclomatica sono inestimabili per me. Ma quando si tratta di cicli di livellamento e dipendenza, sono un po '... ben preoccupato. Patrick Smacchia ha un simpatico post sul blog che descrive l'obiettivo della livellizzazione.

Per essere chiari: Sotto "ciclo di dipendenza" capisco un riferimento circolare tra due spazi dei nomi.

Attualmente sto lavorando su un framework GUI basato su Windows CE per strumenti incorporati - basti pensare alla piattaforma grafica Android ma agli strumenti di fascia bassa. Il framework è un unico assembly con circa 50.000 righe di codice (test esclusi). Il framework è suddiviso nei seguenti spazi dei nomi:

  • Navigazione principale e amp; Sottosistema menu
  • Sottosistema schermo (Presentatori / Visualizzazioni / ...)
  • Controls / Widget Layer

Oggi ho passato la mezza giornata a provare a portare il codice a livelli appropriati [grazie a Resharper nessun problema in generale] ma in tutti i casi esistono alcuni cicli di dipendenza.

Quindi la mia domanda: quanto segue rigorosamente la regola "No Dependency Cycle"? La livellizzazione è davvero così importante?

    
posta ollifant 21.03.2011 - 22:00
fonte

5 risposte

9

Recentemente ho scritto 2 libri bianchi, pubblicati su Simple-Talk sul tema dell'architettura del codice .NET (il primo libro riguarda gli assembly .NET, il secondo riguarda lo spazio dei nomi e la livellizzazione):

Partizionamento del codice base tramite .NET Assemblies e progetti Visual Studio

Definizione di componenti .NET con spazi dei nomi

Is levelization really that important?

Sì, lo è!

Citazione del 2 ° libro bianco:

If the graph of dependencies between components contains a cycle, components involved in the cycle cannot be developed and tested independently. Because of this, the cycle of components represents a super-component, with higher entropy than the sum of the entropies of its contained components.

(...)

As long as the acyclic components constraint is continuously respected, the code base remains highly learnable and maintainable.

  • In traditional building architecture, the gravity strength put the pressure on low level artifacts. This makes them more stable: 'stable' in the sense they are hard to move.
  • In software architecture, abiding by the acyclic component idea put the pressure on low level components. This makes them more stable, in the sense that it is painful to refactor them. Empirically abstractions are less often subject to refactoring than implementations. it is, for this reason, a good idea that low-level components contain mostly abstractions (interfaces and enumerations) to avoid painful refactoring.
    
risposta data 22.03.2011 - 15:26
fonte
6

Non permetto mai le dipendenze circolari tra classi o spazi dei nomi. In C #, Java e C ++ puoi sempre interrompere una dipendenza circolare della classe introducendo un'interfaccia.

Il test di codifica rende difficile l'introduzione di dipendenze circolari.

    
risposta data 22.03.2011 - 01:33
fonte
4

È sempre un tradeof: devi capire il costo delle dipendenze per capire perché le dipendenze devono essere evitate. Allo stesso modo, se comprendi il costo di una dipendenza, puoi decidere se valga la pena nel tuo caso specifico.

Ad esempio, nei videogiochi di console, spesso ci affidiamo alle dipendenze in cui è necessario un rapporto stretto di informazioni, soprattutto per motivi di prestazioni. Va bene, per quanto non dobbiamo essere flessibili come uno strumento di edizione, ad esempio.

Se comprendi i vincoli che il tuo software deve eseguire (essendo hardware, sistema operativo o semplicemente design (come "l'interfaccia utente più semplice che possiamo")) allora dovrebbe essere facile selezionare capire quali dipendenze non dovrebbero essere fatte e di cui uno va bene.

Ma se non hai una ragione buona e chiara, evita qualsiasi dipendenza possibile. Il codice dipendente è l'inferno della sessione di debug.

    
risposta data 22.03.2011 - 00:05
fonte
2

Ogni volta che viene discusso questo argomento, i partecipanti di solito perdono il sito della distinzione tra cicli di costruzione e cicli di esecuzione. Il primo è ciò che John Lakos ha definito "design fisico", mentre il secondo è sostanzialmente irrilevante per la discussione (non rimanere impigliato nei cicli di runtime, come quelli creati dai callback).

Ciò detto, John Lakos era molto severo nell'eliminare tutti i cicli (di costruzione). Bob Martin, tuttavia, ha adottato l'atteggiamento secondo cui solo i cicli tra binari (ad esempio DLL, eseguibili) erano significativi e dovrebbero essere evitati; credeva che i cicli tra le classi all'interno di un binario non fossero terribilmente importanti.

Personalmente ritengo il punto di vista di Bob Martin su questo. Tuttavia, faccio comunque attenzione ai cicli tra le classi perché l'assenza di tali cicli rende il codice più semplice da leggere e da imparare per gli altri.

Va sottolineato, tuttavia, che qualsiasi codice creato con Visual Studio non è in grado di avere dipendenze circolari tra i binari (sia nativi che gestiti). Quindi il problema più grave con i cicli è stato risolto per te. :)

    
risposta data 16.04.2011 - 03:06
fonte
0

Quasi totalmente, perché i cicli di dipendenza di tipo build sono scomodi in Haskell e impossibili in Agda e Coq, e quelle sono le lingue che di solito uso.

    
risposta data 16.04.2011 - 09:49
fonte

Leggi altre domande sui tag