Software Engineering: Some Design Considerations

  1. So far the class declarations, member function definitions, and application program are all stored in one file.

  2. A more conventional way of designing C++ programs is to store class declarations and member definitions in their own separate files. This completely separates the interface part from the implementation part of a class.

  3. Typically, program components are stored in the following fashion:
    • Class declaration are stored in their own header files. The name of the header file is usually the same as the class, with a .h extension. For example, the circle1 class would be declared in the file circle1.h.
    • The member function definitions for a class are stored in a separate .cpp file. The file usually has the same name as the class, with the .cpp extension. For example, the circle1 class's member functions would be defined in the file circle1.cpp. Make sure the class's header file is included using the preprocessor directive:

      #include "circle1.h"

    • Any program that uses the class should #include the class's header file. The class's .cpp file that contains the class's member function definitions should be compiled and linked with the main program. This process can be automated with a project or make utility.

  4. The following example illustrates the above ideas.
    // File circle1.h contains the class declarations only

    class circle1
    {
        private:
            int x_coord;
            int y_coord;
            int radius;
            double area;
            double perimeter;

        public:
            circle1( );
            // constructor

            void set_coord (int x, int y);
            // set the x- and y-coordinates of the center of the circle

            void set_radius (int r);
            // set the radius of the circle

            void compute_area ( );
            // compute the area of the circle

            void compute_perimeter ( );
            // compute the perimeter of the circle

            void display_circle ( ) const;
            // display attributes of the circle

            int get_x_coord ( ) const;

            int get_y_coord ( ) const;

            int get_radius () const;

            double get_area () const;

            double get_perimeter () const;
            // accessor functions
    };



    // File circle1.cpp contains the circle1 class's member function definitions only

    #include <iostream>
    #include "circle1.h" // make sure to include the circle1.h header file

    const double PI = 3.1415926;

    circle1::circle1 ( )
    {
        x_coord = 0;
        y_coord = 0;
        radius = 0;
        area = 0;
        perimeter = 0;
    }

    void circle1::set_coord (int x, int y)
    {
        x_coord = x;
        y_coord = y;
    }

    void circle1::set_radius (int r)
    {
        radius = r;
    }

    void circle1::compute_area ( )
    {
        area = PI * radius * radius;
    }

    void circle1::compute_perimeter ( )
    {
        perimeter = 2 * PI * radius;
    }

    void circle1::display_circle ( ) const
    {
        cout << "x_coordinate is " << x_coord << endl;
        cout << "y_coordinate is " << y_coord << endl;
        cout << "radius is " << radius << endl;
        cout << "area is " << area << endl;
        cout << "perimeter is " << perimeter << endl;
    }

    int circle1::get_x_coord ( ) const
    {
        return x_coord;
    }

    int circle1::get_y_coord ( ) const
    {
        return y_coord;
    }

    int circle1::get_radius () const
    {
        return radius;
    }

    double circle1::get_area () const
    {
        return area;

    }
    double circle1::get_perimeter () const
    {
        return perimeter;
    }

    // File circle1_test.cpp uses and tests the circle1 class

    #include <iostream>
    #include "circle1.h" // make sure to include the circle1.h header file
    using namespace std;

    const double PI = 3.1415926;

    int main ( )
    {
        circle1 my_circle;

        // set circle attributes
        my_circle.set_coord (150, 100);
        my_circle.set_radius (10);

        // compute area and perimeter
        my_circle.compute_area ( );
        my_circle.compute_perimeter ( );

        // display the circle attributes
        cout << "\nThe circle attributes:" << endl;
        my_circle.display_circle ( );

        my_circle.set_radius (100);

        // Although the radius has been changed by the above statement,
        // and so the area and perimeter of the circle should now be different.
        // Unfortunately a flaw in the design of the circle1 class easily allows one to
        // produce incorrect results for the area and perimeter, as illustrated here.

        // display the circle attributes
        cout << "\nThe circle attributes:" << endl;
        my_circle.display_circle ( );

        return 0;
    }

    The output of the above program is shown below:
    
    The circle attributes:
    x_coordinate is 150
    y_coordinate is 100
    radius is 10
    area is 314.159
    perimeter is 62.8319
    
    The circle attributes:
    x_coordinate is 150
    y_coordinate is 100
    radius is 100
    area is 314.159
    perimeter is 62.8319
    
    Notice that the output is not exactly what you want to be.

  5. The class circle1 has a design flaw that allows the radius of the circle to be changed, but the user must remember to update the perimeter and area attributes following such a change. Negligence will result in incorrect values for the perimeter and area.

  6. There are two things one can do to rewrite the circle1 class to make it much safer to use:
    • Make the member functions compute_perimeter and compute_area private so that they are not available to the user to compute the perimeter and area at all.
    • the perimeter and area should be computed automatically every time the circle changes its radius. This can be accomplished by calling the private member functions compute_perimeter and compute_area inside the public member function set_radius, where the radius can be changed.

  7. A better design for the circle class is shown below:
    // File circle2.h contains the class declarations only

    class circle2
    {
        private:
            int x_coord;
            int y_coord;
            int radius;
            double area;
            double perimeter;

            void compute_area ( );
            // private member function to compute the area of the circle

            void compute_perimeter ( );
            // private member function to compute the perimeter of the circle

        public:
            circle2( );
            // constructor

            void set_coord (int x, int y);
            // set the x- and y-coordinates of the center of the circle

            void set_radius (int r);
            // set the circle radius and update the perimeter and area

            void display_circle ( ) const;
            // display attributes of the circle

            int get_x_coord ( ) const;

            int get_y_coord ( ) const;

            int get_radius ( ) const;

            double get_area ( ) const;

            double get_perimeter ( ) const;
            // accessor functions
    };



    // File circle2.cpp contains the circle2 class's member function definitions only

    #include <iostream>
    #include "circle2.h" // make sure to include the circle2.h header file
    using namespace std;

    const double PI = 3.1415926;

    circle2::circle2 ( )
    {
        x_coord = 0;
        y_coord = 0;
        radius = 0;
        area = 0;
        perimeter = 0;
    }

    void circle2::set_coord (int x, int y)
    {
        x_coord = x;
        y_coord = y;
    }

    void circle2::set_radius (int r)
    {
        radius = r;
        // automatically compute the perimeter and area
        compute_perimeter( );
        compute_area( );
    }

    void circle2::compute_area ( )
    {
        area = PI * radius * radius;
    }

    void circle2::compute_perimeter ( )
    {
        perimeter = 2 * PI * radius;
    }

    void circle2::display_circle ( ) const
    {
        cout << "x_coordinate is " << x_coord << endl;
        cout << "y_coordinate is " << y_coord << endl;
        cout << "radius is " << radius << endl;
        cout << "area is " << area << endl;
        cout << "perimeter is " << perimeter << endl;
    }

    int circle2::get_x_coord ( ) const
    {
        return x_coord;
    }

    int circle2::get_y_coord ( ) const
    {
        return y_coord;
    }

    int circle2::get_radius ( ) const
    {
        return radius;
    }

    double circle2::get_area ( ) const
    {
        return area;

    }
    double circle2::get_perimeter ( ) const
    {
        return perimeter;
    }

    // File circle2_test.cpp uses and tests the circle2 class

    #include <iostream>
    #include "circle2.h" // make sure to include the circle2.h header file
    using namespace std;

    const double PI = 3.1415926;

    int main ( )
    {
        circle2 my_circle;

        // set circle attributes
        my_circle.set_coord (150, 100);
        my_circle.set_radius (10);

        // the following way of computing the area and perimeter is illegal,
        // since the member functions are now private,
        // and unnecessary since they are now taken cared of automatically.
        //my_circle.compute_area ( );
        //my_circle.compute_perimeter ( );

        // display the circle attributes
        cout << "\nThe circle attributes:" << endl;
        my_circle.display_circle ( );

        my_circle.set_radius (100);

        // The radius has been changed by the above statement,
        // and the area and perimeter of the circle are immediately and     // automatically updated.

        // display the circle attributes
        cout << "\nThe circle attributes:" << endl;
        my_circle.display_circle ( );

        return 0;
    }

    The output of the program is now given more appropriately as follows:
    
    The circle attributes:
    x_coordinate is 150
    y_coordinate is 100
    radius is 10
    area is 314.159
    perimeter is 62.8319
    
    The circle attributes:
    x_coordinate is 150
    y_coordinate is 100
    radius is 100
    area is 31415.9
    perimeter is 628.319