Sono necessari precondizioni e postcondizioni oltre agli invarianti nelle funzioni dei membri se si progetta per contratto?

StackOverflow https://stackoverflow.com/questions/1219564

Domanda

Comprendo che nel metodo DbC, precondizioni e postcondizioni sono associate a una funzione.

Quello che mi chiedo è se questo vale anche per le funzioni membro.

Ad esempio, supponendo che io usi invarianti all'inizio alla fine di ogni funzione pubblica, una funzione membro sarà simile a questa:

modifica: (ripulito il mio esempio)

void Charcoal::LightOnFire() {
  invariant();
  in_LightOnFire();

  StartBurning();    
  m_Status = STATUS_BURNING;
  m_Color = 0xCCCCCC;

  return; // last return in body

  out_LightOnFire();
  invariant();
}

inline void Charcoal::in_LightOnFire() {
  #ifndef _RELEASE_
  assert (m_Status == STATUS_UNLIT);
  assert (m_OnTheGrill == true);
  assert (m_DousedInLighterFluid == true);
  #endif
}

inline void Charcoal::out_LightOnFire() {
  #ifndef _RELEASE_
  assert(m_Status == STATUS_BURNING);
  assert(m_Color == 0xCCCCCC);
  #endif
}

// class invariant
inline void Charcoal::invariant() {
  assert(m_Status == STATUS_UNLIT || m_Status == STATUS_BURNING || m_Status == STATUS_ASHY);
  assert(m_Color == 0x000000 || m_Color == 0xCCCCCC || m_Color == 0xEEEEEE);
}

Va ??bene usare precondizioni e postcondizioni solo con funzioni globali / generiche e usare solo invarianti all'interno delle classi?

Sembra eccessivo, ma forse il mio esempio è negativo.

modifica:

La postcondizione non sta solo controllando un sottoinsieme dell'invariante?

In quanto sopra, sto seguendo le istruzioni di http://www.digitalmars.com /ctg/contract.html che indica, " L'invariante viene controllato quando un costruttore di classi viene completato, all'inizio del distruttore di classe, prima che venga eseguito un membro pubblico e al termine di una funzione pubblica. "

Grazie.

È stato utile?

Soluzione

Sì.

Il invariante della classe C è una proprietà comune di tutte le sue istanze (oggetti). L'invariante valuta se e solo se l'oggetto si trova in uno stato semanticamente valido.

L'invariante di un ascensore può contenere informazioni come ASSERT (IsStopped () || Door.IsClosed ()) , perché non è valido che un ascensore sia in uno stato diverso da quello fermato (diciamo, salendo) e con la porta aperta.

Al contrario, una funzione membro come MoveTo (int flat) può avere CurrentFlat () == flat come postcondition ; perché dopo una chiamata a MoveTo (6) l'appartamento attuale è 6. Allo stesso modo, potrebbe avere IsStopped () come condizione preliminare , perché (a seconda del design) puoi invoca la funzione SpostaPer se l'ascensore è già in movimento. Innanzitutto, è necessario eseguire una query sul suo stato, assicurarsi che sia stato arrestato, quindi chiamare la funzione.

Ovviamente potrei semplificare totalmente il funzionamento di un ascensore.

In ogni caso, le precondizioni e le postcondizioni non avranno senso, in generale, come condizioni invarianti; un ascensore non deve essere al piano 6 per essere in uno stato valido.

Un esempio più conciso può essere trovato qui: Intercettazione e attributi: un esempio di progetto per contratto di Sasha Goldshtein .

Altri suggerimenti

Limitare i contratti nelle classi agli invarianti non è ottimale.

Precondizioni e Postcondizioni non sono solo un sottoinsieme degli invarianti.

Invarianti, Pre-condizioni e Post-condizioni hanno ruoli molto diversi.

Gli invarianti confermano la coerenza interna dell'oggetto. Dovrebbero essere validi alla fine del costruttore e prima e dopo ogni chiamata del metodo.

Le pre-condizioni stanno verificando che lo stato dell'oggetto e gli argomenti siano adatti all'esecuzione del metodo. I presupposti sono complementari agli invarianti. Coprono il controllo degli argomenti (un controllo più forte che il tipo stesso, cioè non null, > 0, .. ecc.) Ma potrebbero anche verificare lo stato interno dell'oggetto (cioè una chiamata a file.write ( " ; hello " ) è una chiamata valida solo se file.is_rw e file.is_open sono veri).

Le post-condizioni chiedono che il metodo soddisfi i suoi obblighi Le post-condizioni sono anche complementari agli invarianti. Naturalmente lo stato dell'oggetto deve essere coerente dopo l'esecuzione del metodo, ma le Post-condizioni stanno verificando che l'azione prevista sia stata eseguita (cioè list.add (i) dovrebbe avere come conseguenza che list.has (i) è vero e list.count = old list.count + 1).

Bene, il punto di un invariante è che descrive qualcosa che è vero dell'oggetto in ogni momento . In questo caso, qualcosa è sulla griglia o no (niente in mezzo). Normalmente descrivono una proprietà dell'intero stato dell'oggetto.

Le condizioni pre e post descrivono cose che sono vere prima dell'esecuzione di un metodo e subito dopo e riguarderanno solo lo stato che avrebbe dovuto essere toccato dal metodo . Ciò è diverso, presumibilmente, dallo stato dell'oggetto. Le condizioni pre e post potrebbero essere pensate come descrivendo l'impronta di un metodo - proprio quello di cui aveva bisogno, proprio quello che toccava.

Quindi, alla domanda specifica, le idee fanno cose diverse, quindi potresti volere entrambe. Non puoi semplicemente usare gli invarianti invece delle condizioni pre e post: in questo caso, una parte dell'oggetto invariante è " Qualcosa è sulla griglia o no " ;, ma il presupposto di lightOnFire deve sapere che l'oggetto è sulla griglia. Non puoi mai dedurlo dall'oggetto invariante. È vero che da pre e postcondizioni e da uno stato iniziale noto, è possibile (supponendo che la struttura degli oggetti sia mutabile solo tramite metodi e che le condizioni pre e post descrivono tutti i cambiamenti ambientali), inferendo un oggetto invariante. Tuttavia, questo può essere complesso e quando stai affermando cose "in lingua", è più semplice fornire entrambe.

Ovviamente, fare in varianti che affermano che un oggetto booleano è vero o falso è un po 'inutile - il sistema dei tipi lo assicura.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top