An Introduction to Modularity in C++

The concept of modular programming simple. It is an element of programming style that divides a single program into distinct parts called modules, each module being responsible for only one function or set of functions. Modules can also be interchangeable which means that a module can be re-written or replaced without having to make changes to the others. Although some elements of programming practice are subject to personal preference there are some practices that are clearly beneficial.  For that reason it is important to define a programming style that makes sense to all team members who will be playing a part in any programs development.  One major advantage of modularity includes streamlining teamwork by allowing several distinct modules to be written and developed simultaneously.  To maximize on this the exact behaviour of each module is planned ahead of time and specific boundaries are set so that error and conflict is kept to a minimum. We won’t be going to deep here. My goal in writing this article is to introduce C++ modularity at a basic level and to simply show the concept at work.

Here are some questions I had to ask myself. To what extent can a C++ program be broken up into modules? How much modularity should go into the design of your program? Can a program be over-modularized? To answer the first question, probably more than you think. A program can be modularized practically as much as your want. Answering the second question, modularize as much as you can handle so long as it makes sense to you. Think ahead about how you would want to improve or expand your program down the road. It all depends on your goals. To answer to the third question, yes but again think about what makes sense trying to envision all possibilities. Generally I believe over modularizing is not bad so long as it falls into good practice. That way you gain even when it serves no other purpose than practice itself.

For starters let’s look back at my simple credit card validation program made from start to finish with one module. This program takes a number provided by the user, performs some basic arithmetic and determines weather the number is valid or invalid.  Let’s see what happens when we take the the above code and make it “modular” in some imaginary environments.

Suppose that a group of four students were given the task of writing a “credit card validation program” like the one above. The first step to modularizing this code would be to break up the code into functions. Planing ahead we define the functions including their operating parameters and return value. Our planned modules may be something like this,

//doubles then sums all even placed digits, then returns the result as an integer
int sumOfDoubleEvenPlace(const string& cardNumber);
//sums all odd placed digits then returns the result as an integer
int sumOfOddPlace(const string& cardNumber);
//determines validity of cardNumber by calling the above two functions and returning a true or false 
bool isValid(const string& cardNumber);

Then define one variable within the main function accessible to all functions called cardNumber.  Assign one student to each function and one to bring it all together with program control flow and user interaction happening in the main function.

If all goes according to plan, our modular Credit Card Validator program could look like this.

//Author : Daniel Touma (100147753)
//Version  : 0.1
//Name     : Credit Card Number Validator with procedures
//Description : Checks for a numbers adherence to the Luhn algorithm

#include <iostream>
#include <string>

using namespace std;

//function prototypes
int sumOfDoubleEvenPlace(const string& cardNumber);
int sumOfOddPlace(const string& cardNumber);
bool isValid(const string& cardNumber);

int main()
{   //declare variable inside main function
    string cardNumber;

    //welcom message
    cout << "Welcome to the Luhn Algorithm number validation program" << endl;

    //main loop to request user input and call isValid function to validate number. 
    //Exits on user request
    do{
        cout << endl << "Please enter a number to valudate or press q to quit followed by the enter key: ";
        cin >> cardNumber;
        if (cardNumber == "q") break;
            cout << endl << "The number you entered is ";
        if (isValid (cardNumber)== true)
            cout << "valid" << endl;
        else cout << "invalid" << endl;
    }while (cardNumber != "q");

    return 0;
}

//Function to compute and return even place digits
int sumOfDoubleEvenPlace(const string& cardNumber)
{   //variables to be used inside this scope
    int sumOfDoubleEven(0), evenDigit(0), digitPosition;
    for (digitPosition=cardNumber.length()-1; digitPosition>0; digitPosition=digitPosition-2)
    {
        evenDigit=((int)cardNumber[digitPosition-1]-'0')*2;
        evenDigit=evenDigit%10+evenDigit/10;
        sumOfDoubleEven=sumOfDoubleEven + evenDigit;
    }
    return sumOfDoubleEven;
}

//Function to return sum of odd placed digits
int sumOfOddPlace(const string& cardNumber)
{   //variables to be used inside this scope
    int sumOfOdd(0), oddDigit(0), digitPosition;
    for (digitPosition=cardNumber.length(); digitPosition>0; digitPosition=digitPosition-2)
    {
        oddDigit=(int)cardNumber[digitPosition-1]-'0';
        sumOfOdd=sumOfOdd+oddDigit;
    }
    return sumOfOdd;
}

//This function validates the final result of the algorithm and return true or false
bool isValid(const string& cardNumber)
{
    int sum; //the return values of both functions are summed and placed into the sum variable.
    sum=sumOfDoubleEvenPlace(cardNumber)+sumOfOddPlace(cardNumber);
    if (sum%10==0) //executes if sum is divisible by 10
        return true;
    else //executes if sum is not divisible by 10
        return false;
}

Looks good! but what if our program is made up of thousands of lines of code?  There is something we can do to make our program more manageable by organizing our code into distinct files.  In this case three files are all that is needed. A header file ending in .h containing the function prototypes, another with the same name but ending in .cpp containing the functions themselves and finally another containing the main function. The more complex the program the more planing you need to structure it in which case related functions or classes can be grouped together in separate files.

Our header file named cardfunctions.h will look like this.

#ifndef CARDFUNCTIONS_H_INCLUDED
#define CARDFUNCTIONS_H_INCLUDED

#include <iostream>
#include <string>
using namespace std;

//function prototypes
int sumOfDoubleEvenPlace(const string& cardNumber);
int sumOfOddPlace(const string& cardNumber);
bool isValid(const string& cardNumber);

#endif

The source file named cardfunctions.cpp containing the actual functions will look like this.

#include "cardfunctions.h"

//Function to compute and return even place digits
int sumOfDoubleEvenPlace(const string& cardNumber)
{   //variables to be used inside this scope
    int sumOfDoubleEven(0), evenDigit(0), digitPosition; 
    for (digitPosition=cardNumber.length()-1; digitPosition>0; digitPosition=digitPosition-2)
   {
        evenDigit=((int)cardNumber[digitPosition-1]-'0')*2;
        evenDigit=evenDigit%10+evenDigit/10;
        sumOfDoubleEven=sumOfDoubleEven + evenDigit;
    }
    return sumOfDoubleEven;
}

//Function to return sum of odd placed digits
int sumOfOddPlace(const string& cardNumber)
{   //variables to be used inside this scope
    int sumOfOdd(0), oddDigit(0), digitPosition; 
    for (digitPosition=cardNumber.length(); digitPosition>0; digitPosition=digitPosition-2)
    {
        oddDigit=(int)cardNumber[digitPosition-1]-'0';
        sumOfOdd=sumOfOdd+oddDigit;
    }
    return sumOfOdd;
}

//Function to validate number and return true or false
bool isValid(const string& cardNumber)
{
    int sum; //return values of both functions are summed and placed into the sum variable
    sum=sumOfDoubleEvenPlace(cardNumber)+sumOfOddPlace(cardNumber);
    if (sum%10==0) //executes if sum is divisible by 10
        return true;
    else //executes if sum is not divisible by 10
        return false;
}

… and finally the main source file named main.cpp which will look like this.

//Author : Daniel Touma (100147753)
//Version  : 0.1
//Name     : Credit Card Number Validator with procedures
//Description : Checks for a numbers adherence to the Luhn algorithm

#include "cardfunctions.h"

int main()
{   //declare variable inside main function
    string cardNumber;

    //welcome message
    cout << "Welcome to the Luhn Algorithm number validation program" << endl;

    //main loop to request user input and call isValid function to validate number. 
    //Exits on user request.
    do{
        cout << endl << "Please enter a number to valudate or press q to quit followed by the enter key: ";
        cin >> cardNumber;
        if (cardNumber == "q") break;
            cout << endl << "The number you entered is ";
        if (isValid (cardNumber)== true)
            cout << "valid" << endl;
        else cout << "invalid" << endl;
    }while (cardNumber != "q");

    return 0;
}

Now let’s imagine that our Credit Card Validator program is only a small part of a much bigger project of which we have been assigned the following guidelines.

bool cardValidity(const string& cardNumber); //to test a numbers adherence to the Luhn Algorith and produce a true or false return value

In this scenario a simple single function would suffice.  In the situation where thousands of lines of code and multiple classes need to be created then you would plan accordingly. Of course there are always other factors like efficiency that need to be considered. In some cases a balance between modularity and efficiency may need reckoning.  The possibilities demonstrated here only scratch the surface however I hope that sharing what I do know has been a help to someone in their c++ learning experience.

Leave a Reply

Your email address will not be published. Required fields are marked *