Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Design By Contract Technique with an example (http://www.go4expert.com/articles/design-contract-technique-example-t18461/)

Mridula 10Jul2009 14:34

Design By Contract Technique with an example
 

Introduction



This artcile talks about Design By Contract Technique with an example to show how it can be used in an Application.

Background



Most application problems arise because of inappropriate handling of data obtained from external systems that could be a application user, remote server, data base etc.

Defensive Programming prevents any failures or asserts that are going to be triggred, by foreseeing them or figuring out what might go wrong at each stage of the application and thus minimizes the bugs from external system. There are many techinques in this approach and Design by Contract is one among them.

Design By Contract:

This technique enforces the below contracts between blocks or functions of your code:

Pre-conditions:

These are the condtions that must always be True before a block or function of code is enetered. If this condition fails then you can think, that there is a problem with "caller function" code. Pre-conditions have to be checked for internal methods (methods internal to application/module) only.

They should not be checked for public or interface methods, where we need to have some error handling techniuqes to handle wrong inputs from external users.

Examples for Pre-condition checks could be:
Ensure that function parameters are valid before using them in the function.

Post-conditions:

These are the conditions that must hold True once a code block is left, after successfully completing it's assigned task. If this check fails then you can think that there is a problem with "called code".

Unlike to those Pre-conditions, these Post-conditions can be done on both public/interface methods and internal methods. This is to ensure the "called method" has done it's task successuly and returning the valid results too if any.

Example for Post-conditions checks could be:
Testing the validity of a pointer before returning to caller. or
Even the validity of returned results can be done by the "Called method" as part of Post-condition check.

Invariants:

These are the conditions that must hold True, every time the program's execution reaches some points like: between loop passes, between function calls etc. Failure of this test, shows there is a problem with the logic of the program.

Examples for this could be:
1. Prove that an object's state is consistent and valid before operating on itis member
functions.
This test we can have it at the beginning of every member function of the object.
2. Assert for member pointers being not NULL before dereferencing them.

Apart from having implementation of all these conditions, it is always good to explain in brief on these conditions in the code. So, that user/future developer need not inspect the implementations to understand them.

Assertions:
These above conditions can be tested using assertions. So that they are available only during development and debugging time and not in customer load. This is to make sure, above contracts and our assumptions on the program are all True and it is been tested as well to prove the same, before shipping the load to customer.

Here is an Exaple code in C++ to implement the above technique:

The code



In this code, there are total 3 sets of files.

1. myMacros.h - defines the below customized macros

MY_ASSERT
IS_VALID_PTR
SET_OBJECT_OF
IS_OBJECT_OF


2. The respective static methods are implemented in below files
myRobust.cc and myRobust.h

3. Example application program that uses the Design By Contract Technique is:
designByContract.cc

For Invariants:

Here, checking the consistency of object is done - by setting a random number to the object when it gets instaniated and nullifying that value on it's deletion.
SET_OBJECT_OF macro does this.

And then test the value that was set, everytime on entring it's member functions, to make sure "this" pointer is valid.
IS_OBJECT_OF macro does this

For Pre-condition and Post-condition:

Generally to check the validity of pointers before using, that can be tested as part of Pre-condition and Post-condition tests, is done using the macro IS_VALID_PTR.

Customized MY_ASSERT macro is used generally to validate any expression.

Exaplanation on designbyContract.cc:

Here, the implementation of changeName and setName methods shows the usage of this technique.
Invariant condition is checked in all the member functions.
Pre-condition check can be seen in operator = method.

Code: Cpp

// FILE - myMacros.h
//------------------------

#define DEBUG

#include<iostream.h>
#include "myRobust.h"

#ifndef DEBUG
  #define IS_VALID_PTR(ptr)
#else
  #define IS_VALID_PTR(ptr) \
    MyRobust::isValidPointer((unsigned int)ptr)
#endif

#ifndef DEBUG
  #define MY_ASSERT(x)
#else
  #define MY_ASSERT(x) \
    MyRobust::logMsg(x, #x, __FILE__, __LINE__)
#endif

#ifndef DEBUG
  #define SET_OBJECT_OF(instValue, thisPtr)
#else
  #define SET_OBJECT_OF(instValue, thisPtr) \
    MyRobust::setObjectOf(instValue, (unsigned int) thisPtr, (thisPtr)->myInstValue())
#endif

#ifndef DEBUG
  #define IS_OBJECT_OF(instValue, thisPtr)
#else
  #define IS_OBJECT_OF(instValue, thisPtr) \
    MyRobust::isObjectOf(instValue, (unsigned int)thisPtr, thisPtr->getInstanceValue())
#endif

// FILE - myRobust.h
//--------------------

#ifndef __MyRobust_H_INCLUDED__
#define __MyRobust_H_INCLUDED__

#include <iostream.h>

class MyRobust
{
    MyRobust();
    MyRobust(const MyRobust & obj);

  public:
    static bool logMsg(bool x, const char *msg, char* file, unsigned int line);
    static bool isValidPointer(unsigned int ptr);
    static bool setObjectOf(unsigned int instValue, unsigned int ptr, unsigned int& myInstValue);
    static bool isObjectOf(unsigned int instValue, unsigned int ptr, unsigned int myInstValue);
};

// FILE - myRobust.cc - For implementation of static methods
//-----------------------------------------------------------------

#include "myRobust.h"
#include "myMacros.h"

#define nullValue 0x1111

bool MyRobust::logMsg(bool x, const char *msg, char* file, unsigned int line)
{
  if( false == x)
  {
    cout<<"On line " <<line<<":";
    cout<<" in file " <<file <<":";
    cout<<" Error !! Assert "<<msg << " failed\n\n";
    abort();
    return(true);//---
  }
  else
  {
    return(true);//---
  }
}

bool MyRobust::setObjectOf(unsigned int instValue, unsigned int ptr, unsigned int& myInstValue)
{
  MY_ASSERT((0 != ptr) && (NULL != ptr));

  if(0 == instValue)
  {
    cout<<"myInstValue ID is not correct...\n";
    return (false);
  }
  else
  {
    //set myInstValue
    myInstValue = instValue;
    return (true);
  }
}

bool MyRobust::isObjectOf(unsigned int instValue, unsigned int ptr, unsigned int myInstValue)
{
  MY_ASSERT(0 != ptr && NULL != ptr);

  if(instValue != myInstValue)
  {
    cout<<"Current InstValue: "<<myInstValue<<" and Refered InstValue: "<<instValue<<" does not match\n";
    return (false);
  }
  else
  {
    return (true);
  }
}

bool MyRobust::isValidPointer(unsigned int ptr)
{
  if ((ptr == 0) && (NULL == ptr))
  {
    return(false);//---
  }
  else
  {
    return(true);//---
  }
}

// FILE - designByContract.cc - Example for using in an Application
//-------------------------------------------------------------------------

#include<iostream.h>
#include "myMacros.h"
#include "myRobust.h"

#define nullValue 0x1111

class MyClass
{
  unsigned int myInstValue_m;
  char * name;

  public:
    //accessors
    unsigned int getInstanceValue();
    unsigned int &myInstValue();

    //constructors
    MyClass();
    MyClass(const MyClass & obj);
   
    //destructor
    ~MyClass();

    //assignment operator
    MyClass & operator =(const MyClass &obj);
   
    void dispName();
    void deleteName();
    void changeName(char *name);
    void setName(char *name);
};

unsigned int MyClass::getInstanceValue(void)
{
  return (myInstValue_m); //---
}

unsigned int & MyClass::myInstValue(void)
{
  return (myInstValue_m); //---
}

MyClass::MyClass()
{
  //set the random number
  SET_OBJECT_OF(0x10, this);

  name = new char[8];
  strcpy(name, "Mridula");
}

MyClass::MyClass(const MyClass & obj)
{
  //set the random number
  SET_OBJECT_OF(0x10, this);

  name = new char[8];
  name = obj.name;
}

MyClass &
MyClass::operator =(const MyClass &obj)
{
  //check the validity of object
  MY_ASSERT(IS_OBJECT_OF(0x10, this));
  cout<<"\nEntering MyClass::operator =\n";

  //Pre-condition check - obj != this
  MY_ASSERT(this != &obj);

  name = new char[8];
  name = obj.name;
 
  return(*this); //---
}

MyClass::~MyClass()
{
  if(name)
  {
    delete (name);
  }

  //nullify the value
  SET_OBJECT_OF(nullValue, this);
}

void MyClass::dispName()
{
  //check the validity of object
  MY_ASSERT(IS_OBJECT_OF(0x10, this));
  cout<<"\nEntering MyClass::dispName\n";
 
  //check the validity of pointer name
  MY_ASSERT(IS_VALID_PTR(name));
  cout<<"Name is: "<<name<<"\n";
}

void MyClass::changeName(char *name)
{
  //check the validity of object
  MY_ASSERT(IS_OBJECT_OF(0x10, this));
  cout<<"\nEntering MyClass::changeName\n";

  if(NULL != name)
  {
    //calling an internal method to set the name
    setName(name);

    //This check can be done here as well, if it is not done in setName method.
    //Post-condition check on the result
    //MY_ASSERT(IS_VALID_PTR(name));
  }
  else
  {
    cout<<"Please input a proper name...\n";
  }
}

void MyClass::setName(char * newName)
{
  //check the validity of object
  MY_ASSERT(IS_OBJECT_OF(0x10, this));
  cout<<"\nEntering MyClass::setName\n\n";

  //Pre-condition check on argument
  MY_ASSERT(IS_VALID_PTR(newName));

  deleteName();
  name = new char(sizeof(newName)+1);

  //change the name
  strcpy(name, newName);
 
  //Post-condition check on the result
  MY_ASSERT(IS_VALID_PTR(name));
}

void MyClass::deleteName(void)
{
  //check the validity of object
  MY_ASSERT(IS_OBJECT_OF(0x10, this));
  cout<<"\nEntering MyClass::deleteName\n\n";
 
  //check the validity of pointer name
  MY_ASSERT(IS_VALID_PTR(name));
  delete (name);
  name=NULL;
}

int main()
{
  unsigned int input;
  cout<<"\nDesign By Contract Design Technique...\n\n";
 
  cout <<"Please enter a valid number: \n";
  cout<<"1 - Invariant Check - To check the validity of an Instance \n";
  cout<<"2 - Pre and Post condition usage example \n";
  cout<<"3 - Pre-condition Check - To check the self assignment\n";
  cout<<"4 - Post-condition Check - To check the validity of a Pointer\n";
  cout<<"5 - Error Handling for wrong input in public function\n";
  cin>>input;
  cout<<"\n";
  cout << "You entered the number " << input << ".\n\n";

  switch(input)
  {
    case 1:
      {
        MyClass *obj1;
        obj1 = new MyClass();
        obj1->dispName();

        delete (obj1);

        cout<<"\nTrying to access the func from Object that is deleted ...\n";
           
        //calling the func on invalid obj
        obj1->dispName();

        break; //---
      }
    case 2:
      {
        MyClass *obj2;
        obj2 = new MyClass();
   
        cout<<"Current name is:\n";
        cout<<"----------------------\n";
        obj2->dispName();
        cout<<"\n";

        char *name = new char(sizeof("Manaswita"));
        strcpy(name, "Manaswita");

        obj2->changeName(name);
        cout<<"Changed the name to:\n";
        cout<<"----------------------\n";
        obj2->dispName();
        cout<<"\n";

        delete(name);
        delete(obj2);

        break; //---
      }
    case 3:
      {
        MyClass obj2;

        cout<<"\nTrying to Assign to the same object ...\n";
        obj2 = obj2;

        break;//----
      }
    case 4:
      {
        MyClass *obj2;
        obj2 = new MyClass();
        obj2->dispName();
   
        obj2->deleteName();
        cout<<"\nTrying to display the name which is NULL ...\n";
        obj2->dispName();

        delete(obj2);

        break; //---
      }

    default:
      {
        MyClass *obj2;
        obj2 = new MyClass();
   
        cout<<"\nTrying to change the name to NULL ...\n";
        char *name;
        name = NULL;
        obj2->changeName(name);

        delete(obj2);

        break; //---
      }
  }

  return (0); //---
}

1. output:
--------------
Design By Contract Design Technique...

Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
1

You entered the number 1.


Entering MyClass::dispName
Name is: Mridula

Trying to access the func from Object that is deleted ...
Current InstValue: 4369 and Refered InstValue: 16 does not match
On line 91: in file designByContract.cc: Error !! Assert IS_OBJECT_OF(0x10, this) failed

Abort (core dumped)


2. output:
----------------
Design By Contract Design Technique...

Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
2

You entered the number 2.

Current name is:
----------------------

Entering MyClass::dispName
Name is: Mridula


Entering MyClass::changeName

Entering MyClass::setName


Entering MyClass::deleteName

Changed the name to:
----------------------

Entering MyClass::dispName
Name is: Manaswita


3. output:
---------------
Design By Contract Design Technique...

Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
3

You entered the number 3.


Trying to Assign to the same object ...

Entering MyClass::operator =
On line 69: in file designByContract.cc: Error !! Assert this != &obj failed

Abort (core dumped)


4. output:
-------------
Design By Contract Design Technique...

Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
4

You entered the number 4.


Entering MyClass::dispName
Name is: Mridula

Entering MyClass::deleteName


Trying to display the name which is NULL ...

Entering MyClass::dispName
On line 95: in file designByContract.cc: Error !! Assert IS_VALID_PTR(name) failed

Abort (core dumped)


5. output:
--------------
Design By Contract Design Technique...

Please enter a valid number:
1 - Invariant Check - To check the validity of an Instance
2 - Pre and Post condition usage example
3 - Pre-condition Check - To check the self assignment
4 - Post-condition Check - To check the validity of a Pointer
5 - Error Handling for wrong input in public function
5

You entered the number 5.


Trying to change the name to NULL ...

Entering MyClass::changeName
Please input a proper name...

References



ASSERTs in C++

shabbir 3Aug2009 14:34

Re: Design By Contract Technique with an example
 
Nominate this article for Article of the month - Jul 2009

Juanita99 25Aug2009 13:07

Re: Design By Contract Technique with an example
 
this looks great and really easy to follow - thanks a lot for posting and sharing it.

Mridula 25Aug2009 16:55

Re: Design By Contract Technique with an example
 
many thanks Juanita99 !!

zim22 23Oct2009 21:06

Re: Design By Contract Technique with an example
 
Please, answer some questions about "Design by contract" methodology:
1) I can not (from "Liskov Substitution Principle" point of view) create base class Scales and then refine its function in derived classes KitchenScales and IndustrialScales, because subclasses don't behave equally, when using them through pointer to base class. But this two classes KitchenScales and IndustrialScales are very similar, so that's naturally(for me) to create abstract base class for them... How would you express their similarity?

2) If contracts(pre|postconditions) don't specify for base abstract Scales. How do they look to derived class? They are stronger, weaker?

*sorry for my English... :)

Code:

#include <cassert>

// abstract base class. defines interface for derived classes
class Scales {
public:
  virtual void putOnScales(int weight) = 0; // which post/preconditions?
  // if I don't specify post/preconditions for this base class - are they
  // the most weaker or the most strongest?
protected:
  Scales() : weight_(0) { }
  int weight_;
};
//------------------------------------------------------------
class KitchenScales : public Scales {
public:
  KitchenScales()  { }
  virtual void putOnScales(int weight) {
    // precoditions
    // this scales only carries weight < 100 pounds
    assert(weight < 100);

    weight_ = weight;

    // postconditions == preconditions
    // ensure, that weight is correct(<100 pounds)
    assert(weight_ < 100);
  }
};
//------------------------------------------------------------
class IndustrialScales : public Scales {
public:
  IndustrialScales()  { }
  virtual void putOnScales(int weight) {
    // precoditions
    // this scales carries weight < 100000 pounds
    assert(weight < 100000);

    weight_ = weight;

    // postconditions == preconditions
    // ensure, that weight is correct(<100000 pounds)
    assert(weight_ < 100000);
  }
};
//------------------------------------------------------------
int main()
{
  KitchenScales ks;
  ks.putOnScales(50);

  IndustrialScales is;
  is.putOnScales(10000);

  Scales *arr[2] = {&ks, &is};
  arr[0]->putOnScales(500); // invokes assert from KitchenScales::putOnScales
  arr[1]->putOnScales(500); // OK

  return 0;
}


zim22 23Oct2009 21:13

Re: Design By Contract Technique with an example
 
addition to my 2 question:
***
DbC rule says:
Quote:

"...when redefining a routine [in a derivative], you may only replace its
precondition by a weaker one, and its postcondition by a stronger one."
I think preconditions for base class can't be the weakest, because then I cannot define preconditions for derived classes (because they would be stronger, than in a base class).
But how can I define strong preconditions for abstract base class?...

Mridula 2Nov2009 11:01

Re: Design By Contract Technique with an example
 
Sorry for the delay in reply!!

For your first question!!

Here the Design seems to be not correct. I don't see any difference between Base class Derived classes. So need to revist Design here.

For your second question
2) If contracts(pre|postconditions) don't specify for base abstract Scales. How do they look to derived class? They are stronger, weaker?

For pure virtual functions, where in you do not have definitions for the functions and when you override in Derived class, we need to have strong Pre-Condition and Post condition that are specific to Derived one.

And for your continuation of second question i.e.
I think preconditions for base class can't be the weakest, because then I cannot define preconditions for derived classes (because they would be stronger, than in a base class).
But how can I define strong preconditions for abstract base class?...


Pre-Post conditions for Base class should not be week at all. In case if we have already defined Pre-conditions for Base class function, then when it gets overidden in it's derived class, then we should go for Week Pre-conditions here in Derived class function.

We cannot define Pre and Post conditions for ABC!!

Hope all the questions are answered!!
thanks.

doctorweb 8Nov2009 18:16

there are any problem below:
Example for Post-conditions checks could be:
Testing the validity of a pointer before returning to caller. or
Even the validity of returned results can be done by the "Called method" as part of Post-condition check.

Mridula 9Nov2009 14:32

Re: Design By Contract Technique with an example
 
Yes. Both can be done.
i.e. Checking the validity of a pointer just before returnng to callee in called method or
checking it in callee itself just before using it.

I think first way better!!

zim22 12Nov2009 18:27

Re: Design By Contract Technique with an example
 
>Hope all the questions are answered!!
Thanks for answers! But unfortunately I haven't understood a few things...

>Here the Design seems to be not correct. I don't see any difference between Base class Derived classes. So need to revist Design here.
I see difference. It is weight, that scales can carry out.
KitchenScales: max. weight 100 pounds
IndustrialScales: max. weight 100000000 pounds

And these two classes have the same interface, so either of them can be used through ABC.
For example, you have two classes:
1) Cow
2) Chicken
And they both have similar function: heart_beat()
Why not connect them through ABC "Breather" with abstract function heart_beat()?
I think my example with Scales are the same...

>We cannot define Pre and Post conditions for ABC!!
I meant defining Pre-Post conditions not for ABC, but for its functions.

From LSP:
Quote:

...In order for the LSP to hold, and with it the Open-Closed principle, all derivatives
must conform to the behavior that clients expect of the base classes that they use.
Clients would use classes KitchenScales and IndustrialScales not directly, but through ABC Scales.
And If this ABC doesn't have any behavior, just function's signature, then about what behavior, that clients will be expect, we are talking about? So, I think abstract base functions must have pre|post conditions.

Please, correct me, if I am wrong.


All times are GMT +5.5. The time now is 14:47.