Con quale rigore segui la regola del "ciclo di non dipendenza" (NDepend)


10

Un po 'di background: come capo di una squadra utilizzo NDepend circa 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 preziose per me. Ma quando si tratta di cicli di livellamento e dipendenza sono un po '... ben preoccupato. Patrick Smacchia ha un bel post sul blog che descrive l'obiettivo della livellamento.

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

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

  • Sottosistema di navigazione e menu principale
  • Sottosistema schermo (presentatori / visualizzazioni / ...)
  • Controlli / Widget Layer

Oggi ho trascorso mezza giornata nel tentativo di portare il codice a livelli adeguati [grazie a Resharper nessun problema in generale] ma in tutti i casi esistono alcuni cicli di dipendenza.

Quindi la mia domanda: con quale rigore segui la regola "Nessun ciclo di dipendenza"? La livellamento è davvero così importante?


2
Se "nessun ciclo di dipendenza" significa nessun riferimento circolare, allora - nessuna scusa. Quelli sono puro male.
Arnis Lapsa,

@ollifant: definisci cosa intendi esattamente quando scrivi "Nessun ciclo di dipendenza". Tra livelli o tra classi all'interno di un livello?
Doc Brown,

Risposte:


9

Di recente ho scritto 2 libri bianchi, pubblicati su Simple-Talk sul tema dell'architettura del codice .NET (il primo libro riguarda gli assemblaggi .NET, il secondo uno sugli spazi dei nomi e la livellizzazione):

Partizionare la tua base di codice tramite .NET Assembly e Visual Studio Projects

Definizione di componenti .NET con spazi dei nomi

La livellamento è davvero così importante?

Sì!

Citazione dal secondo libro bianco:

Se il grafico delle dipendenze tra i componenti contiene un ciclo, i componenti coinvolti nel ciclo non possono essere sviluppati e testati in modo indipendente. Per questo motivo, il ciclo dei componenti rappresenta un supercomponente, con entropia superiore alla somma delle entropie dei suoi componenti contenuti.

(...)

Fintanto che il vincolo dei componenti aciclici viene continuamente rispettato, la base di codice rimane altamente apprendibile e mantenibile.

  • Nell'architettura tradizionale dell'edificio, la forza di gravità esercita una pressione su artefatti di basso livello. Questo li rende più stabili: "stabili" nel senso che sono difficili da spostare.
  • Nell'architettura software, attenersi all'idea del componente aciclico esercita pressioni su componenti di basso livello. Questo li rende più stabili, nel senso che è doloroso riformattarli. Le astrazioni empiriche sono meno spesso soggette a refactoring rispetto alle implementazioni. è, per questo motivo, una buona idea che componenti di basso livello contengano principalmente astrazioni (interfacce ed enumerazioni) per evitare dolorosi refactoring.

6

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

La codifica test-first rende difficile introdurre dipendenze circolari.


4

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

Ad esempio, nei videogiochi per console, spesso facciamo affidamento su dipendenze in cui sono necessarie strette relazioni di informazioni, principalmente per motivi di prestazioni. Va bene per quanto non dobbiamo essere flessibili come uno strumento di edizione, ad esempio.

Se capisci i vincoli che il tuo software deve eseguire (sia hardware, sistema operativo o solo progettazione (come "l'interfaccia utente più semplice che possiamo")), dovrebbe essere facile selezionare capire quali dipendenze non dovrebbero essere fatte e quali sono ok.

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


2

Ogni volta che viene discusso questo argomento, i partecipanti di solito perdono il sito della distinzione tra cicli di build-time e run-time. Il primo è quello che John Lakos ha definito "disegno fisico", mentre il secondo è sostanzialmente irrilevante per la discussione (non rimanere impigliati nei cicli di runtime, come quelli creati dai callback).

Detto questo, John Lakos era molto severo nell'eliminare tutti i cicli (tempo di costruzione). Bob Martin, tuttavia, adottò l'atteggiamento secondo cui solo i cicli tra binari (ad esempio DLL, file eseguibili) erano significativi e andrebbero evitati; credeva che i cicli tra le classi all'interno di un binario non fossero tremendamente importanti.

Personalmente sostengo l'opinione di Bob Martin su questo. Faccio comunque ancora attenzione ai cicli tra le classi perché l'assenza di tali cicli rende il codice più facile da leggere e da imparare per gli altri.

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


0

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.