Perché dovrei usare i contratti di codice


26

Di recente mi sono imbattuto nel framework Microsoft per i contratti di codice.

Ho letto un po 'di documentazione e mi sono trovato costantemente a chiedere: "Perché dovrei mai voler fare questo, in quanto non è e spesso non è possibile eseguire un'analisi statica".

Ora, ho già una sorta di stile di programmazione difensiva, con eccezioni di protezione come questa:

if(var == null) { throw new NullArgumentException(); }

Sto anche usando NullObject Pattern molto e raramente ho problemi. Aggiungi i test unitari e sei pronto.

Non ho mai usato assert e non li ho mai persi. Al contrario. Odio davvero il codice che contiene molte affermazioni insignificanti, che è solo rumore per me e mi distrae da ciò che voglio davvero vedere. I contratti di codice, almeno alla maniera di Microsoft, sono più o meno gli stessi, e anche peggio. Aggiungono molto rumore e complessità al codice. Nel 99% verrà comunque generata un'eccezione, quindi non mi interessa se proviene dall'asserzione / contratto o dal problema reale. Rimangono solo pochissimi casi in cui gli stati del programma vengono realmente danneggiati.

Quindi, francamente, quali sono i vantaggi dell'utilizzo dei contratti di codice? Ce n'è affatto? Se usi già i test unitari e il codice in modo difensivo, ritengo che l'introduzione di contratti non valga il costo e metta in rumore il codice che un manutentore maledirà quando aggiorna quel metodo, proprio come faccio quando non riesco a vedere cosa sta facendo il codice a causa di affermazioni inutili. Devo ancora vedere una buona ragione per pagare quel prezzo.



1
Perché dici "non è e spesso non è possibile eseguire un'analisi statica"? Ho pensato che fosse uno dei punti di questo progetto (al contrario di usare semplicemente Assert o lancio difensivo di eccezioni)?
Carson63000,

@ Carson63000 Leggi attentamente la documentazione e scoprirai che il correttore statico emetterà un sacco di avvertimenti, la maggior parte non necessari (gli sviluppatori ammettono che sia così) e che i contratti più definiti genereranno un'eccezione e non faranno nulla .
Falcon,

@Falcon: stranamente, la mia esperienza è completamente diversa. L'analisi statica non funziona in modo impeccabile, ma piuttosto bene, e raramente vedo avvisi inutili, anche quando lavoro al livello di avviso 4 (il più alto).
Arseni Mourzenko,

Immagino che dovrò provarlo per scoprire come si comporta.
Falcon,

Risposte:


42

Potresti anche aver chiesto quando la digitazione statica è migliore della digitazione dinamica. Un dibattito che infuria da anni senza fine. Quindi, dai un'occhiata a domande come studi di lingue tipicamente o dinamicamente

Ma l'argomento di base sarebbe il potenziale per scoprire e risolvere problemi in fase di compilazione che altrimenti sarebbero scivolati in produzione.

Contratti contro guardie

Anche con guardie ed eccezioni, si è ancora affrontati il ​​problema che il sistema non riuscirà a svolgere il compito previsto in qualche caso. Forse un'istanza che sarà piuttosto critica e costosa fallire.

Contratti contro test unitari

Il solito argomento su questo è che i test dimostrano la presenza di bug, mentre i tipi (contratti) dimostrano l'assenza. F.ex. usando un tipo sai che nessun percorso nel programma potrebbe fornire un input non valido, mentre un test potrebbe solo dirti che il percorso coperto ha fornito l'input corretto.

Contratti contro modello di oggetti nulli

Ora questo è almeno nello stesso parco di palline. Lingue come Scala e Haskell hanno avuto un grande successo con questo approccio per eliminare i riferimenti null interamente dai programmi. (Anche se Scala consente formalmente i null, la convenzione è di non usarli mai)

Se si utilizza già questo modello per eliminare gli NRE, in pratica è stata rimossa la più grande fonte di errori di runtime, in pratica esiste il modo in cui i contratti consentono di farlo.

La differenza potrebbe essere che i contratti hanno un'opzione per richiedere automaticamente tutto il tuo codice per evitare null, e quindi ti costringono a usare questo modello in più punti per passare la compilazione.

Inoltre, i contratti ti offrono anche la flessibilità di indirizzare le cose oltre il nulla. Quindi, se non vedi più alcun NRE nei tuoi bug, potresti voler usare i contratti per strangolare il prossimo problema più comune che potresti avere. Off da uno? Indice fuori portata?

Ma...

Detto questo. Sono d'accordo sul fatto che i contratti sul rumore sintattico (e persino sul rumore strutturale) si aggiungano al codice è piuttosto sostanziale e l'impatto che l'analisi ha sul tempo di costruzione non deve essere sottovalutato. Quindi, se decidete di aggiungere contratti al vostro sistema, sarebbe probabilmente saggio farlo con molta attenzione focalizzandosi su quale classe di bug si cerca di risolvere.


6
Questa è un'ottima prima risposta per i programmatori. Sono contento di avere la tua partecipazione alla comunità.

13

Non so da dove provenga l'affermazione secondo cui "non è e spesso non può eseguire un'analisi statica". La prima parte dell'asserzione è chiaramente sbagliata. Il secondo dipende da cosa intendi per "spesso". Preferirei dire che spesso esegue analisi statiche e raramente fallisce. Nell'applicazione aziendale ordinaria, raramente diventa molto più vicino a mai .

Quindi ecco che arriva, il primo vantaggio:

Vantaggio 1: analisi statica

Le asserzioni ordinarie e il controllo degli argomenti presentano uno svantaggio: vengono posticipati fino all'esecuzione del codice. D'altro canto, i contratti di codice si manifestano a un livello molto precedente, sia durante la fase di codifica sia durante la compilazione dell'applicazione. Prima si rileva un errore, meno costoso è risolverlo.

Vantaggio 2: una specie di documentazione sempre aggiornata

I contratti di codice forniscono anche una sorta di documentazione che è sempre aggiornata. Se il commento XML del metodo SetProductPrice(int newPrice)dice che newPricedovrebbe essere superiore o uguale a zero, potresti sperare che la documentazione sia aggiornata, ma potresti anche scoprire che qualcuno ha cambiato il metodo in modo da newPrice = 0lanciare un ArgumentOutOfRangeException, ma non ha mai cambiato la documentazione corrispondente. Data la correlazione tra i contratti di codice e il codice stesso, non si ha il problema della documentazione non sincronizzata.

Anche il tipo di documentazione fornita dai contratti di codice è prezioso in un modo che spesso i commenti XML non spiegano bene i valori accettabili. Quante volte mi chiedevo se nullo string.Emptyo \r\nè un valore autorizzato per un metodo, e i commenti XML sono rimasti in silenzio su questo!

In conclusione, senza contratti di codice, molti pezzi di codice sono così:

Accetterò alcuni valori ma non altri, ma dovrai indovinare o leggere la documentazione, se presente. In realtà, non leggere la documentazione: è obsoleta. Basta scorrere tutti i valori e vedrai quelli che mi fanno generare eccezioni. Devi anche indovinare l'intervallo di valori che possono essere restituiti, perché anche se ti dico un po 'di più su di loro, potrebbe non essere vero, date le centinaia di modifiche apportate a me negli ultimi anni.

Con i contratti di codice, diventa:

L'argomento title può essere una stringa non nulla con una lunghezza di 0..500. L'intero che segue è un valore positivo, che può essere zero solo quando la stringa è vuota. Infine, restituirò un IDefinitionoggetto, mai null.

Vantaggio 3: contratti di interfacce

Un terzo vantaggio è che i contratti di codice potenziano le interfacce. Diciamo che hai qualcosa del tipo:

public interface ICommittable
{
    public ICollection<AtomicChange> PendingChanges { get; }

    public void CommitChanges();

    ...
}

Come garantiresti che, usando solo asserzioni ed eccezioni, CommitChangespossa essere chiamato solo quando PendingChangesnon è vuoto? Come garantiresti che PendingChangesnon lo sia mai null?

Vantaggio 4: applicare i risultati di un metodo

Infine, il quarto vantaggio è essere in grado di ottenere Contract.Ensurei risultati. Cosa succede se, quando si scrive un metodo che restituisce un numero intero, voglio essere sicuro che il valore non sia mai inferiore o uguale a zero? Di cui cinque anni dopo, dopo aver subito molti cambiamenti da molti sviluppatori? Non appena un metodo ha più punti di ritorno, Assertdiventa un incubo per la manutenzione.


Considera i contratti di codice non solo come mezzo di correttezza del tuo codice, ma un modo più rigoroso per scrivere il codice. Allo stesso modo, una persona che ha usato linguaggi esclusivamente dinamici potrebbe chiedersi perché dovresti applicare i tipi a livello linguistico, mentre puoi fare la stessa cosa in asserzioni quando necessario. Puoi, ma la digitazione statica è più facile da usare, meno soggetta a errori rispetto a una serie di affermazioni e autodocumentazione.

La differenza tra la tipizzazione dinamica e la tipizzazione statica è estremamente vicina alla differenza tra la programmazione ordinaria e la programmazione per contratto.


3

So che questo è un po 'tardi alla domanda, ma c'è un altro vantaggio nei contratti di codice che merita di essere menzionato

I contratti sono controllati al di fuori del metodo

Quando abbiamo condizioni di guardia all'interno del nostro metodo, questo è il luogo in cui si verifica il controllo e dove vengono segnalati errori. Potrebbe quindi essere necessario esaminare la traccia dello stack per scoprire dove si trova l'origine effettiva dell'errore.

I contratti di codice si trovano all'interno del metodo, ma i presupposti vengono controllati al di fuori del metodo quando qualcosa tenta di chiamarlo. I contratti diventano parte del metodo e determinano se può essere chiamato o meno. Ciò significa che viene segnalato un errore molto più vicino alla fonte effettiva del problema.

Se si dispone di un metodo protetto da contratto che viene richiesto per molti luoghi, questo può essere un vero vantaggio.


2

Due cose davvero:

  1. Supporto per gli strumenti, VS può rendere i dati del contratto separati dall'intelligenza in modo che faccia parte dell'aiuto del completamento automatico.
  2. Quando una sottoclasse ha la precedenza su un metodo, perdi tutti i tuoi assegni (che dovrebbero comunque essere validi se segui l'LSP) con contratti che seguono automaticamente le loro classi secondarie.
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.