2010-05-04 17 views
10

Io sono l'attuazione mia ArrayList a fini scolastici, ma per rendere le cose un po 'che sto cercando di utilizzare C# 4.0 Code Contracts. Tutto andava bene finché non avevo bisogno di aggiungere Contratti ai costruttori. Devo aggiungere Contract.Ensures() nel costruttore di parametri vuoto?Design by contratti e costruttori

public ArrayList(int capacity) { 
     Contract.Requires(capacity > 0); 
     Contract.Ensures(Size == capacity); 

     _array = new T[capacity]; 
    } 

    public ArrayList() : this(32) { 
     Contract.Ensures(Size == 32); 
    } 

Direi di sì, ogni metodo dovrebbe avere un contratto ben definito. D'altra parte, perché metterlo se si tratta solo di delegare il lavoro al costruttore "principale"? Logicamente, non avrei bisogno di farlo.

L'unico punto che vedo in cui sarebbe utile definire in modo esplicito il contratto in entrambi i costruttori è se in futuro abbiamo il supporto Intelisense per i contratti. Se ciò accadesse, sarebbe utile essere espliciti su quali contratti ha ciascun metodo, come quello che appare in Intelisense.

Inoltre, ci sono dei libri intorno che andare un po 'più a fondo sui principi e l'utilizzo di design by contract? Una cosa è avere la conoscenza della sintassi di come utilizzare i contratti in un linguaggio (C#, in questo caso), altro è sapere come e quando utilizzarlo. Ho letto diverse tutorial e Jon Skeet C# in profondità articolo su di esso, ma mi piacerebbe andare un po 'più in profondità, se possibile.

Grazie

+0

correlati: http://stackoverflow.com/questions/2539497/code-contracts-do-we-have-to-specify-contract-requires-statements-redundant/2626997 – porges

+0

Si potrebbe sbarazzarsi del "Contratto". Richiede (capacità> 0); " se fai il c'tor prendi un uint contro un int. Cerco di usare Contracts come ultima risorsa, in cui la lingua limita la tua capacità di far sapere al prossimo sviluppatore cosa stavi pensando quando hai creato il codice in primo luogo. Se si decide di mantenere il contratto, vorrei scrivere "Contract.Requires (capacità> = 0);" per uno dovrebbe sempre essere in grado di costruire una struttura di dati vuota e quindi avere la possibilità di aggiungere oggetti in un secondo momento. –

+0

"... in futuro abbiamo il supporto Intelisense per i contratti." Il futuro è ora! http://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970 –

risposta

5

Sono completamente in disaccordo con la risposta di Thomas. Finché si stanno facendo scelte nella realizzazione di ArrayList(), si dovrebbe avere un contratto per esso che documentano queste scelte.

Qui, state facendo la scelta di chiamare il costruttore principale con argomento 32. Ci sono molte altre cose che potreste aver deciso di fare (non solo riguardo alla scelta della dimensione di default). Dando un contratto a ArrayList() che è quasi identico a quello dei documenti ArrayList(int) che hai deciso di non fare la maggior parte delle sciocchezze che avresti potuto fare invece di chiamarlo direttamente.

La risposta "chiama il costruttore principale, quindi lascia che il contratto del costruttore principale faccia il lavoro" ignora completamente il fatto che il contratto è lì per salvarti dal dover esaminare l'implementazione. Per una strategia di verifica basata sul controllo di asserzione in fase di esecuzione, lo svantaggio di scrivere contratti anche per costruttori/metodi brevi che chiamano quasi direttamente un altro costruttore/metodo è che si finisce per controllare le cose due volte. Sì, sembra ridondante, ma il controllo dell'assertion in fase di esecuzione è solo una strategia di verifica, ei principi di DbC sono indipendenti da esso. Il principio è: se può essere chiamato, ha bisogno di un contratto per documentare ciò che fa.

+1

Sì; e se usi il controllo statico ti dirà questo :) – porges

0

Umh, non capiscono fino in fondo il motivo per cui si mettono i 'Garantisce' anche nel c'tor di default. Perché chiama il principale c'tor, che già implementa il contratto completo, il lettore di default lo fa altrettanto bene - per definizione. Quindi questa è una ridondanza logica, e quindi un grande 'Do not'. Forse potrebbe avere implicazioni pragmatiche, come dici tu - non sai Code Contracts che i buoni ...

Per quanto riguarda la letteratura - le fonti migliori sono:

HTH! Thomas

+0

Supponendo che un giorno Intelisense supporti completamente i Contratti di codice (o presumendo che ad oggi, esamineremo la documentazione dei contratti XML), affermando esplicitamente che il contratto del costruttore no-args avrebbe il beneficio di consentire al cliente di conoscere il contratto. Se lo lasci vuoto non puoi. E questo è il motivo per cui ho creato questo post. –

+1

Capisco la logica, e forse questo diventerà vero un giorno ... Ma facendo sth. perché in futuro potrebbero esserci dei benefici si è rivelata una delle peggiori (e più costose) cose mai nello sviluppo del software. Fai sempre la cosa più semplice che faccia il lavoro! –

+0

Sì, hai un punto. Che cosa succede se Intelisense ha supportato questa funzione oggi? Avrebbe quindi senso inserire quel contratto nel costruttore no args? –

1

codice client (utilizzando contratti di codice) che utilizza ArrayList non saprà che il vuoto costruttore Ensure s che Size == 32 meno che non si affermano in modo esplicito in modo da utilizzare un Ensure.

Così (per esempio):

var x = new ArrayList(); 
Contract.Assert(x.Size == 32) 

vi darà l'avviso "non affermare la provata".

È necessario indicare esplicitamente tutti i contratti; il codice dei contratti masterizzatore checker/static non "guardare attraverso" un metodo per vedere le eventuali implicazioni — vedere my answer to the related question "Do we have to specify Contract.Requires(…) statements redundantly in delegating methods?"

0

Design by Contract proviene dalle radici matematiche della programmazione funzionale: Preconditions e Postconditions.

Non hai davvero bisogno di un libro su di esso, è al massimo un capitolo di una laurea in Informatica (la maggior parte insegnerà il concetto). La premessa di base è scrivere le precondizioni che la funzione si aspetta e l'output che produrrà dato i parametri corretti. Non ci si aspetta che la funzione funzioni con parametri iniziali errati. Lo stesso si può dire per un algoritmo: è infallibile, cioè è garantito per fornire il risultato atteso.

Ecco come mi è stato insegnato nel grado che sto studiando attualmente, ci potrebbero essere definizioni migliori intorno però. L'articolo di Wikipedia su Design per contratto è scritto con un taglio OO, ma le pre/post-condizioni sono indipendenti dalla lingua.

+0

Sì, il mio punto è che sulla carta il design OO non ha molto da imparare. Scopri le classi, l'ereditarietà e l'incapsulamento e sembra che non sia molto più da imparare. Ma in pratica ci sono tonnellate di libri su come realizzare buoni programmi OO. A volte conoscere la sintassi e la "teoria" non è sufficiente, solo quello. –

+0

Questi sono probabilmente i libri Wrox e apress che riguardano la praticità :) Molto più la profondità è insegnata in un (discreto) grado –

+0

Non ho una laurea, quindi cosa faccio? –

1

Raccomando di leggere Object Oriented Software Construction, 2nd Edition o forse Touch of Class, entrambi da Bertrand Meyer. In alternativa, è possibile leggere l'articolo del 1992 Applying "Design by Contract" dello stesso autore.

In sintesi:

  • Il invariante di classe devono essere in possesso dopo il costruttore (uno di essi) finiture, e prima e dopo l'esecuzione qualsiasi metodo pubblico della classe.
  • Le precondizioni di metodo e postconditions sono condizioni aggiuntive che devono essere valide per entrare e uscire da qualsiasi metodo pubblico, insieme all'invariant.

Quindi, nel vostro caso, concentratevi sull'invarianza. Produce un oggetto corretto (uno che soddisfi l'invariante di classe), indipendentemente dal costruttore richiamato.

In questo related answer ho discusso argomenti simili, incluso un esempio.