First Step to OOP as an Undergraduate

Sanjuka Ravishan
MS Club of SLIIT
Published in
9 min readMay 20, 2023

Welcome to the fascinating world of Object Oriented Programming! If you’re an enthusiastic undergraduate student and new to OOP concepts? If yes, I hope this article would help you understand the basics of object-oriented concepts. OOP is essential in modern software development, offering powerful tools to solve complex problems and organize code effectively. Let’s delve into the essence of OOP by using a real-world example to bring its concepts to life.

Imagine you’re building a smart home system, where various devices like lights, security cameras, and thermostats need to work seamlessly together. Initially, you might tackle this challenge by writing separate blocks of code for each device, duplicating similar functionalities, and making the system harder to manage and maintain.

Here’s where Object Oriented Programming comes to the rescue! OOP enables us to conceptualize the smart home system as a collection of objects, each representing a specific device. By organizing code into objects, we can encapsulate their data (properties) and behavior (methods) within a blueprint known as a class.

Classes and Objects

Let’s take the example of a smart light bulb. The class representing this device would define its properties, such as brightness level and color, along with its behavior, such as turning on and off, adjusting brightness, and changing color. This encapsulation of properties and behaviors within a class allows us to create multiple instances (objects) of the smart light bulb, each with its own unique characteristics and functionality.

Abstraction

Let’s revisit the example of a smart light bulb to understand abstraction in action.

When we interact with a smart light bulb, we don’t need to know the complicated details of how it works internally. We’re presented with a simplified interface that allows us to turn it on or off, adjust brightness, and change colors with a few taps on our smartphone app. This abstraction shields us from the underlying complexities of the light bulb’s circuitry and communication protocols.

In the field of OOP, abstraction enables us to define a class for the smart light bulb that exposes only the necessary methods for interaction. The class abstracts away the inner workings of the light bulb, providing a clear and understandable interface for users or other parts of the program to interact with.

For instance, our smart light bulb class may have methods like turnOn(), turnOff(), adjustBrightness(), and changeColor(). These methods encapsulate the underlying implementation details, such as controlling the electrical circuits and communicating with the smartphone app. As users of the class, we can interact with the light bulb object through these simple and abstracted methods without having to deal with the complexities of its internal mechanisms.

In summary, we can call abstraction in OOP as hiding unnecessary complexities and focusing on the essential features of an object.

Encapsulation

When it comes to the smart light bulb, there are certain data elements and actions that we want to encapsulate(sum up) within the class. For instance, the brightness level, color, and power status are specific attributes that are relevant to the light bulb object.

Encapsulation allows us to define these attributes as private members within the class. This means that they are not directly accessible from outside the class. Instead, we provide controlled access to them through public methods or properties.

Here, if we want to interact with privately defined members inside a class, we need public methods to access them and we also called these public methods that can help to connect with private members inside a class as interfaces.

For instance, we can have private variables like “brightnessLevel”, “color” and “isOn” within the smart light bulb class. These variables store the specific state and information related to the light bulb. To interact with or modify these private variables, we create public methods such as “setBrightness()”, “setColor()” and “turnOn()”.

In summary, we can call Encapsulation in OOP about bundling data and behavior within a class, protecting data integrity, and controlling access through defined methods.

Getters and Setters

Getters and setters, also known as accessors and mutators, are essential components of encapsulation in OOP. They provide controlled access to the private attributes of a class, allowing us to retrieve and modify their values. Let’s revisit our smart light bulb example to understand the role of getters and setters.

In the smart light bulb class, we may have private attributes like “brightnessLevel,” “color,” and “isOn” that store specific information about the light bulb. Getters and setters are used to interact with these attributes.

A getter, such as “getBrightness()” retrieves the current value of the brightness level. It allows external entities to access and retrieve the brightness level of the light bulb object. For instance, if we want to check the current brightness level of the light bulb, we can use the getter method to obtain that information.

A setter, such as “setBrightness()” allows us to modify the brightness level of the light bulb. It provides a controlled way to update the brightness level, ensuring that any necessary validations or adjustments are applied. For example, if we want to increase or decrease the brightness level of the light bulb, we can use the setter method to set the new value.

Coding Part

We have discussed a lot with that dumb smart light bulb example. Now see how to use those examples to create simple code snippets to easily understand these basic concepts. Here I will continue with cpp language as most of you are following the basics of oop with cpp ;)

In C++ we generally separate each class implementation into two files. A Header file contains the class definitions including public methods and the second file is .cpp file containing the implementation of the class methods. In our smartBulb example, these two files would be, SmartBulb.h as the header file and SmartBulb.cpp as the method implementation file. Also in general we have our separate main.cpp file and we use it to create objects of the class we have previously implemented.

First, we create the class definition inside our SmartBulb.h header file. Here common convention and recommended practice is to begin class names with an uppercase letter,

class SmartBulb{

private:
float brightnessLevel;
char color[10];
bool isOn;

public:
void setBrightnessLevel(float p_brightnessLevel);
float getBrightnessLevel();
void setColor(const char p_newColor[]);
bool isLightOn();
void turnOn();
void turnOff();
};

Now we have created the class definition using public methods. Then we can implement these public methods inside our SmartBulb.cpp file. In here don’t forget to include SmartBulb.h header file at the very beginning of your method implementation file.

#include <iostream> 
#include "SmartBulb.h"
using namespace std;

// Implementing public methods

void SmartBulb::setBrightnessLevel(float p_brightnessLevel) {
brightnessLevel = p_brightnessLevel;
}

float SmartBulb::getBrightnessLevel() {
return brightnessLevel;
}

void SmartBulb::setColor(const char p_newColor[]) {
strcpy_s(color, p_newColor);
}

bool SmartBulb::isLightOn() {
return isOn;
}

void SmartBulb::turnOn() {
isOn = true;
cout << "bulb turned on";
}

void SmartBulb::turnOff() {
isOn = false;
cout << "bulb turned off";
}

Yeah, when you write method implementation, you have to follow the way below :|

ReturnType ClassName::methodName(methodParameters) {

// Method implementation goes here :)
}

eg:-

void SmartBulb::setBrightnessLevel(float p_brightnessLevel) {

brightnessLevel = p_brightnessLevel;
}

After that, our class definition and implementation part is done. If we want to create objects using this class and want to control their behaviors, that part we do inside the main.cpp file. Also, don’t forget to include SmartBulb.h header file in the main.cpp file as well.

#include <iostream>
#include "SmartBulb.h"
using namespace std;

int main(){

//creating objects.
SmartBulb bedRoomBulb, officeRoomBulb, kitchenBulb, garageBulb,outdoorBulb;

//controll the behavior of object, bedRoomBulb.
bedRoomBulb.isLightOn(); //check whether the light is on.
bedRoomBulb.turnOn();
bedRoomBulb.setColor("warmWhite");
bedRoomBulb.setBrightnessLevel(40); //set the brightness to 40%.
bedRoomBulb.turnOff(); //turn off the bulb.

return 0;
}

Finally, we are done with making all three files that we needed to implement to understand how classes are created using object-oriented concepts. Also, separating this class implementation into separate files like this promotes code reuse, speeds up the compilation, and enables clean and scalable codebases.

Inheritance

Then let's see what is inheritance. Assume that you creating a class called securityBulb in your header file. It is also a smart bulb, right? So can’t we use those properties and behaviors in our SmartBulb class for our securityBulb class or do we need another turnOn() and turnOff() method implementation for the security bulb? No..we can use the inheritance concept here.

Inheritance is like children inheriting traits from their parents. In coding, it’s when one class shares its abilities with another class.

To understand this, let's create our new child class(derived) called securityBulb. To show the inheritance from a parent class(base) use,

class childClassName : public parentClassName{}

and since we are accessing the properties of our base class SmartBulb, we can’t keep them as private as further. As we need to access them inside child classes we make its access specifier to protected.

//Base class
class SmartBulb{

protected:
float brightnessLevel;
char color[10];
bool isOn;

public:
void turnOn();
void turnOff();
void setBrightnessLevel(float p_brightnessLevel);
// ... other methods ...
};

//Derived class
class SecurityBulb : public SmartBulb{

private:
bool isMotionDetected;

public:
void setIsMotionDetected(bool isMotion);
void motionAction();

};

The role of the security bulb is that if it detects a motion within the range of distance, it should turn on automatically. For that let's implement sample methods inside our method implementation .cpp file for the securityBulb.

#include <iostream>
using namespace std;

void SmartBulb::turnOn() {
isOn = true;
cout << "bulb turned on";
}

void SmartBulb::turnOff() {
isOn = false;
cout << "bulb turned off";
}

//setter
void SecurityBulb::setIsMotionDetected(bool isMotion) {
isMotionDetected = isMotion;
}

void SecurityBulb::motionAction() {
if (isMotionDetected) {
turnOn(); //inherit from SmartBulb class
} else {
turnOff(); //inherit from SmartBulb class
}
}

int main(){

SecurityBulb *securityBulb01 = new SecurityBulb();
securityBulb01->setIsMotionDetected(true);
securityBulb01->motionAction(); //output: "bulb turned on"

return 0;
}

Thanks to inheritance, we were able to use the turnOn() and turnOff() methods of the SmartBulb class for our SecurityBulb <3

Polymorphism

“Polymorphism” means having many forms. having many forms for what? for objects? yes.. In C++, it’s like using one name to represent multiple actions. This lets you call methods in different classes using the same name, adapting to the specific situation automatically. Just like a person can have different roles in different scenarios, objects in C++ can take various roles through polymorphism.

How about we name our new object as “motionBulb” and classify it as a security bulb? The motionBulb’s main function is to adjust its brightness depending on the proximity of any detected motion, which also helps conserve energy. Let’s set the bulb to illuminate at 50% brightness if the motion is detected within 5 meters or less, and at 100% brightness if the motion is beyond 5 meters.

Our primary objective is here to override the "setBrightnessLevel()" method in our base class and convert it to an energy-saving method in the SecurityBulb class. For that, we customize our override method according to our needs and make sure the return type is the same as the base class.

#include <iostream>
using namespace std;

class SmartBulb {
protected:
float brightnessLevel;
// ... other members ...
public:
virtual void setBrightnessLevel(float p_brightnessLevel);
// ... other methods ...
};

class SecurityBulb : public SmartBulb {
public:
void setBrightnessLevel(float distance) override;
// ... other methods ...
};

//----- method implementation -----------------
void SmartBulb::setBrightnessLevel(float p_brightnessLevel) {
brightnessLevel = p_brightnessLevel;
}

void SecurityBulb::setBrightnessLevel(float distance) {
if (distance <= 5 && distance >= 0) {
brightnessLevel = 50.0f;
cout << "bulb turn on for distance<5 with brightness 50%" << endl;
} else {
brightnessLevel = 100.0f;
cout << "bulb turn on for distance>5 with brightness 100%" << endl;
}
}
// ... other method implentations ...


int main(){
// Using base class pointer to point to a derived class object
SmartBulb *motionBulb = new SecurityBulb();

//calls derived's setBrightnessLevel()
motionBulb -> setBrightnessLevel(15.0f);

return 0;
}

By marking the setBrightnessLevel() method as virtual in the base class, we are indicating to the compiler that this method can be overridden by derived classes. Here, we're creating a base class pointer motionBulb that points to a SecurityBulb object. When calling motionBulb->setBrightnessLevel(15.0f);, because the setBrightnessLevel() method is declared as virtual in the base class and overridden in the derived class, the derived class's method will be called dynamically at runtime.

output:

bulb turn on for distance>5 with brightness 100%

End :/

We have taken the first step into the Object Oriented Concepts by exploring some fundamental concepts using the example of a smart light bulb. We’ve covered abstraction, encapsulation, inheritance, polymorphism, and the importance of separate class implementation files. However, the journey doesn’t end here.

To unlock the full potential of OOP, encourage you to delve deeper into other essential concepts such as constructors, destructors, how to handle pointers effectively, and more. Exploring these topics on your own will enhance your understanding and skills as an undergraduate student.

Also, remember everything takes time when it is your first time. Take the time to discover what other basic concepts you should know, and actively seek out resources and examples to strengthen your grasp of OOP principles.

Thank you for being a part of my first blog article. Till next time, happy coding! 💻

--

--