Design By Contract Technique with an example

Discussion in 'C' started by Mridula, Jul 10, 2009.

  1. Mridula

    Mridula New Member

    Joined:
    Mar 5, 2008
    Messages:
    316
    Likes Received:
    19
    Trophy Points:
    0
    Occupation:
    S/w proffessional
    Location:
    Bangalore

    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:
    // 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++
     
  2. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
  3. Juanita99

    Juanita99 New Member

    Joined:
    Aug 25, 2009
    Messages:
    1
    Likes Received:
    0
    Trophy Points:
    0
    this looks great and really easy to follow - thanks a lot for posting and sharing it.
     
  4. Mridula

    Mridula New Member

    Joined:
    Mar 5, 2008
    Messages:
    316
    Likes Received:
    19
    Trophy Points:
    0
    Occupation:
    S/w proffessional
    Location:
    Bangalore
    many thanks Juanita99 !!
     
  5. zim22

    zim22 New Member

    Joined:
    Oct 23, 2009
    Messages:
    3
    Likes Received:
    0
    Trophy Points:
    0
    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;
    }
     
  6. zim22

    zim22 New Member

    Joined:
    Oct 23, 2009
    Messages:
    3
    Likes Received:
    0
    Trophy Points:
    0
    addition to my 2 question:
    ***
    DbC rule says:
    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?...
     
  7. Mridula

    Mridula New Member

    Joined:
    Mar 5, 2008
    Messages:
    316
    Likes Received:
    19
    Trophy Points:
    0
    Occupation:
    S/w proffessional
    Location:
    Bangalore
    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.
     
  8. doctorweb

    doctorweb New Member

    Joined:
    Nov 8, 2009
    Messages:
    1
    Likes Received:
    0
    Trophy Points:
    0
    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.
     
  9. Mridula

    Mridula New Member

    Joined:
    Mar 5, 2008
    Messages:
    316
    Likes Received:
    19
    Trophy Points:
    0
    Occupation:
    S/w proffessional
    Location:
    Bangalore
    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!!
     
  10. zim22

    zim22 New Member

    Joined:
    Oct 23, 2009
    Messages:
    3
    Likes Received:
    0
    Trophy Points:
    0
    >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:
    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.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice