I am the kind of person who can ignore scattered and untidy surroundings, but when it comes to messy code, I cannot resist cleaning and refactoring it.
This blog mostly covers the aspect of code refactoring.
Let's get started.
Suppose we've built a Random Coordinates Generator in C. If we put all our code in a single file, it'll look like this. Output:
The code is quite self-explanatory, just give it a read you'll get the idea of what's going on.
Refactoring Begins
To get started on refactoring, firstly, we'll separate the code in .c
and header .h
files, adding function signature/declaration in .h
file and function definition in .c
file.
Header File is a file that contains function declarations and macro definitions.
Now, the question is, Why header files? Why not only separate .c
files?
Abstractionnn...
To create reliable and robust code bases, we need to follow some principles, one of which is abstraction.
Hiding implementation makes the code more maintainable and less dependent.
After refactoring, our file tree will look like this:
coordinate.h
file doesn't know the implementation of the declared functions.
random_coordinates.c
uses functions from coordinate.h
but does not know its implementation details.
Look how clean is our main()
function now.
Our main.c
file also does not know the implementation of print_random_coordinates()
as only the header file is included.
Now, comes the question: coordinate.c
and random_coordinates.c
are not included anywhere, so how come our program gets to know their implementation?
During compilation, we link both the required .c
files with the main.c
file.
gcc main.c coordinate.c random_coordinates.c -o main
A couple of lines of code that start with #ifndef FILENAME
may be causing you an itch in the brain. Those are known as "include guards".
Why and How to use "INCLUDE Guards"?
Suppose we have two headers and the first.h
includes second.h
and the main.c
include both first.h
and second.h
Now, this will cause the main.c
to have a double implementation of the functions in second.h
.
To avoid this, we use "include guards":
Usage:
#ifndef UNIQUE_NAME
#define UNIQUE_NAME
function_signature()
#endif
These macros do the following:
If UNIQUE_NAME is not defined before then,
define UNIQUE_NAME and include the header
If this header is included for second time in the same scope,
then the #ifndef
condition is not satisfied, and the lines after #ifndef
are not read as #endif
is executed just after #ifndef
.
I hope you learned something. Be sure to leave a comment! Check out my github repo for this code.