21
NovWhat are Object-Oriented Programming Concepts?
Object-Oriented Programming Concepts
Object-Oriented Programming (OOP)is important for developers looking to create flexible and efficient applications. Imagine organizing code into reusable objects, allowing you to create, manage, and modify applications with ease. OOPs enables encapsulation, inheritance, polymorphism, and abstraction, making it easier to model real-world problems and create scalable, maintainable solutions without added complexity.
In this OOPs tutorial, I’ll explain the key concepts of object-oriented programming, how they simplify software development by enabling modular design, and why mastering them is key to creating robust, efficient applications.
What is object-oriented programming?
- Object-Oriented Programming (OOP) is a way for you to organize your code by thinking of it like real-life objects.
- It helps you group things together, so your code is easier to work with and reuse.
- With OOPs, you can use encapsulation to keep some parts of your code hidden and only show what’s necessary.
- Inheritance lets you take parts of old code and reuse it for new things.
- Polymorphism helps your code change depending on the situation, and abstraction makes things simple by showing just what’s needed and hiding extra details.
- Using OOPs concepts makes it easier for you to build and manage big projects because it breaks problems into smaller, simpler pieces.
Before exploring OOPs, it is important for you to understand the basics of method declaration. Knowing these prerequisites will help you grasp how to create and use methods effectively in your programming. This knowledge forms a strong foundation that supports your understanding of OOP concepts.
Pre-requisites
Let’s look at what you need to know about method declaration in a simpler way. A method declaration has six main parts:
1. Access Modifier
This tells you who can use the method in your program. There are four types:
- Public: Anyone can use it.
- Protected: Only classes in the same package and subclasses can use it.
- Private: Only the class that has the method can use it.
- Default: If you don’t use any modifier, it can be used within the same class and package.
2. Return Type
- This is the type of value the method will give back.
- If the method doesn’t return anything, you use void.
3. Method Name
- This is how you name the method.
- There are some rules for naming, but it’s a bit different from naming fields.
4. Parameter List
- This is where you list the inputs the method needs.
- You write the type of each input followed by its name, separated by commas.
- If there are no inputs, you just use empty parentheses ( ).
5. Exception List
- This is where you mention any errors the method might cause.
6. Method Body
- This is the block of code that does the work of the method.
- It’s enclosed in curly braces { }.
Let’s explore the fundamental concepts of Object-Oriented Programming (OOP) and discover how they can enhance your coding skills and make application development easier.
OOPs Concepts
Following are the OOPs Concepts:
- Class
- Objects
- Data Abstraction
- Encapsulation
- Inheritance
- Polymorphism
- Dynamic Binding
- Message Passing
1. Class
- A class is like a blueprint for creating objects.
- It defines the properties (what things have) and methods (what things can do) that the objects created from it will have.
- Using classes helps you organize your code and keep it neat.
- In a class, you can specify unique features that each object will have while also sharing common behavior.
- This makes it easier to create similar objects without repeating code.
- When designing a class, it’s essential to think about what features and actions will be needed for the objects that will use it.
- A well-designed class helps you manage your code better and makes it easier to understand.
- Overall, classes are a fundamental part of organizing and building software efficiently.
Explanation
- This diagram shows a class named Car, which acts as a blueprint for creating car objects.
- It has three attributes: color, model, and year, which describe the car's features.
- The class also includes three methods: start(), stop(), and honk(), which define what actions a car can perform.
2. Objects
- An object is like a specific example made from a class blueprint.
- It has its own properties (like color and size) and can perform actions (like run or jump) defined in the class.
- Using objects helps you use the features of a class while giving each one unique values.
- When you create an object, it gets the shared behavior from the class but can also have its own specific details.
- This makes it simple to manage similar items without rewriting code.
- Thinking about what properties and actions your object needs is important when creating it.
- A well-made object helps keep your code organized and clear, making it easier to work with.
- Overall, objects are key to building and organizing software effectively.
Example
#include <iostream>
#include <string>
using namespace std;
class Car {
string color;
string model;
int year;
public:
// Constructor
Car(string c, string m, int y) : color(c), model(m), year(y) {}
// Methods
void start() {
cout << model << " is starting." << endl;
}
void stop() {
cout << model << " is stopping." << endl;
}
void honk() {
cout << model << " is honking." << endl;
}
};
int main() {
Car car1("Red", "Honda City", 2020);
Car car2("Blue", "Hyundai Creta", 2021);
car1.start();
car2.honk();
return 0;
}
class Car {
String color;
String model;
int year;
// Constructor
Car(String color, String model, int year) {
this.color = color;
this.model = model;
this.year = year;
}
// Methods
void start() {
System.out.println(model + " is starting.");
}
void stop() {
System.out.println(model + " is stopping.");
}
void honk() {
System.out.println(model + " is honking.");
}
}
public class Main {
public static void main(String[] args) {
Car car1 = new Car("Red", "Honda City", 2020);
Car car2 = new Car("Blue", "Hyundai Creta", 2021);
car1.start();
car2.honk();
}
}
using System;
class Car {
public string Color { get; }
public string Model { get; }
public int Year { get; }
// Constructor
public Car(string color, string model, int year) {
Color = color;
Model = model;
Year = year;
}
// Methods
public void Start() {
Console.WriteLine($"{Model} is starting.");
}
public void Stop() {
Console.WriteLine($"{Model} is stopping.");
}
public void Honk() {
Console.WriteLine($"{Model} is honking.");
}
static void Main(string[] args) {
Car car1 = new Car("Red", "Honda City", 2020);
Car car2 = new Car("Blue", "Hyundai Creta", 2021);
car1.Start();
car2.Honk();
}
}
class Car:
def __init__(self, color, model, year):
self.color = color
self.model = model
self.year = year
def start(self):
print(f"{self.model} is starting.")
def stop(self):
print(f"{self.model} is stopping.")
def honk(self):
print(f"{self.model} is honking.")
# Creating objects of Car
car1 = Car("Red", "Honda City", 2020)
car2 = Car("Blue", "Hyundai Creta", 2021)
# Using methods on objects
car1.start()
car2.honk()
class Car {
constructor(color, model, year) {
this.color = color;
this.model = model;
this.year = year;
}
start() {
console.log(`${this.model} is starting.`);
}
stop() {
console.log(`${this.model} is stopping.`);
}
honk() {
console.log(`${this.model} is honking.`);
}
}
// Creating objects of Car
const car1 = new Car("Red", "Honda City", 2020);
const car2 = new Car("Blue", "Hyundai Creta", 2021);
// Using methods on objects
car1.start();
car2.honk();
Output
Honda City is starting.
Hyundai Creta is honking.
Explanation
- The
Car
class has three private attributes:color
,model
, andyear
. - A constructor initializes these attributes when a new
Car
object is created. - The class includes three methods:
start
,stop
, andhonk
, which print messages to indicate the car's actions. - In the
main
function, twoCar
objects are created:car1
andcar2
, with different colors and models. - The program calls the
start
method oncar1
and thehonk
method oncar2
, producing the corresponding output.
3. Data Abstraction
- Data abstraction is like simplifying something by hiding the complicated parts and showing only what’s important.
- It helps you concentrate on what an object does rather than how it does it.
- By using data abstraction, you create an easy way to use your objects without needing to know all the details.
- This keeps your code tidy and minimizes mistakes.
- When building a system, consider which details can be kept hidden and what users need to know.
- Good data abstraction helps you handle complexity and makes your code simpler to understand and manage.
- Overall, data abstraction is key to creating organized and effective software.
Example
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
void color() {
cout << "The shape is colored." << endl;
}
};
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing a Circle." << endl;
}
};
class Square : public Shape {
public:
void draw() override {
cout << "Drawing a Square." << endl;
}
};
int main() {
Shape* circle = new Circle();
Shape* square = new Square();
circle->draw();
circle->color();
square->draw();
square->color();
delete circle;
delete square;
return 0;
}
using System;
abstract class Shape
{
public abstract void Draw();
public void Color()
{
Console.WriteLine("The shape is colored.");
}
}
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a Circle.");
}
}
class Square : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a Square.");
}
}
class Program
{
static void Main()
{
Shape circle = new Circle();
Shape square = new Square();
circle.Draw();
circle.Color();
square.Draw();
square.Color();
}
}
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def draw(self):
pass
def color(self):
print("The shape is colored.")
class Circle(Shape):
def draw(self):
print("Drawing a Circle.")
class Square(Shape):
def draw(self):
print("Drawing a Square.")
circle = Circle()
square = Square()
circle.draw()
circle.color()
square.draw()
square.color()
abstract class Shape {
abstract void draw();
void color() {
System.out.println("The shape is colored.");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing a Circle.");
}
}
class Square extends Shape {
@Override
void draw() {
System.out.println("Drawing a Square.");
}
}
public class Main {
public static void main(String[] args) {
Shape circle = new Circle();
Shape square = new Square();
circle.draw();
circle.color();
square.draw();
square.color();
}
}
class Shape {
draw() {
throw "Method not implemented.";
}
color() {
console.log("The shape is colored.");
}
}
class Circle extends Shape {
draw() {
console.log("Drawing a Circle.");
}
}
class Square extends Shape {
draw() {
console.log("Drawing a Square.");
}
}
const circle = new Circle();
const square = new Square();
circle.draw();
circle.color();
square.draw();
square.color();
Explanation
- The
Shape
class is an abstract class with a pure virtual functiondraw
and a concrete methodcolor
. - The
draw
the method must be implemented by any derived class whilecolor
provides a default implementation. - The
Circle
andSquare
classes inherit fromShape
and provide their own implementations of thedraw
method. - In the
main
function, pointers to the base classShape
are used to create objects ofCircle
andSquare
. - The program calls the
draw
method on both shapes, followed by thecolor
method on the circle object, producing the corresponding output. - Finally, the dynamically allocated memory for the shapes is released using.
delete
.
4. Encapsulation
- Encapsulation is like putting the details of how something works inside a box and only showing what’s necessary on the outside.
- It keeps the data safe and prevents outside parts from changing it directly.
- With encapsulation, you create rules for how the data can be accessed or modified.
- This means you can protect the object’s internal state and ensure it behaves correctly.
- When designing your classes, think about which data should be hidden and how users will interact with it.
- Good encapsulation helps you maintain control over your objects and makes your code more reliable and easier to understand.
- Overall, encapsulation is essential for building strong and organized software.
Example
#include <iostream>
using namespace std;
class BankAccount {
private:
double balance;
public:
BankAccount(double initialBalance) {
balance = initialBalance;
}
void deposit(double amount) {
if (amount > 0) {
balance += amount;
cout << "Deposited: " << amount << endl;
} else {
cout << "Deposit amount must be positive." << endl;
}
}
double getBalance() {
return balance;
}
};
int main() {
BankAccount myAccount(1000.0);
myAccount.deposit(500.0);
cout << "Current balance: " << myAccount.getBalance() << endl;
return 0;
}
using System;
class BankAccount
{
private double balance;
public BankAccount(double initialBalance)
{
balance = initialBalance;
}
public void Deposit(double amount)
{
if (amount > 0)
{
balance += amount;
Console.WriteLine("Deposited: " + amount);
}
else
{
Console.WriteLine("Deposit amount must be positive.");
}
}
public double GetBalance()
{
return balance;
}
}
class Program
{
static void Main()
{
BankAccount myAccount = new BankAccount(1000.0);
myAccount.Deposit(500.0);
Console.WriteLine("Current balance: " + myAccount.GetBalance());
}
}
class BankAccount:
def __init__(self, initial_balance):
self.balance = initial_balance
def deposit(self, amount):
if amount > 0:
self.balance += amount
print(f"Deposited: {amount}")
else:
print("Deposit amount must be positive.")
def get_balance(self):
return self.balance
my_account = BankAccount(1000.0)
my_account.deposit(500.0)
print("Current balance:", my_account.get_balance())
class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
balance = initialBalance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: " + amount);
} else {
System.out.println("Deposit amount must be positive.");
}
}
public double getBalance() {
return balance;
}
}
public class Main {
public static void main(String[] args) {
BankAccount myAccount = new BankAccount(1000.0);
myAccount.deposit(500.0);
System.out.println("Current balance: " + myAccount.getBalance());
}
}
class BankAccount {
constructor(initialBalance) {
this.balance = initialBalance;
}
deposit(amount) {
if (amount > 0) {
this.balance += amount;
console.log("Deposited: " + amount);
} else {
console.log("Deposit amount must be positive.");
}
}
getBalance() {
return this.balance;
}
}
const myAccount = new BankAccount(1000.0);
myAccount.deposit(500.0);
console.log("Current balance: " + myAccount.getBalance());
Output
Deposited: 500
Current balance: 1500
Explanation
- In this code, we define a BankAccount class with a private balance variable. We initialize the balance through the constructor and can deposit money using the deposit() method, which ensures the amount is positive before updating the balance. We can retrieve the current balance with the getBalance() method. In the main function, we create a BankAccount, deposit money, and print the updated balance.
Inheritance
- Inheritance is like creating a new object by building on top of an existing one and reusing its properties and methods instead of starting from scratch.
- It allows you to create a hierarchy where new classes inherit the behaviors and features of existing classes.
- With inheritance, you can extend or customize the functionality of a base class while keeping its core elements.
- This helps reduce code duplication and makes it easier to manage changes. If the base class changes, those updates can be applied to all the derived classes.
- When designing your classes, think about which behaviors should be shared and what can be extended.
- Good use of inheritance keeps your code efficient, organized, and easier to maintain.
- Inheritance is a key tool for creating scalable and flexible software systems.
Example
#include <iostream>
using namespace std;
class Animal {
public:
void eat() {
cout << "This animal is eating." << endl;
}
};
class Dog : public Animal {
public:
void bark() {
cout << "The dog is barking." << endl;
}
};
int main() {
Dog myDog;
myDog.eat();
myDog.bark();
return 0;
}
using System;
class Animal
{
public void Eat()
{
Console.WriteLine("This animal is eating.");
}
}
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("The dog is barking.");
}
}
class Program
{
static void Main()
{
Dog myDog = new Dog();
myDog.Eat();
myDog.Bark();
}
}
class Animal:
def eat(self):
print("This animal is eating.")
class Dog(Animal):
def bark(self):
print("The dog is barking.")
my_dog = Dog()
my_dog.eat()
my_dog.bark()
class Animal {
void eat() {
System.out.println("This animal is eating.");
}
}
class Dog extends Animal {
void bark() {
System.out.println("The dog is barking.");
}
}
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.eat();
myDog.bark();
}
}
class Animal {
eat() {
console.log("This animal is eating.");
}
}
class Dog extends Animal {
bark() {
console.log("The dog is barking.");
}
}
const myDog = new Dog();
myDog.eat();
myDog.bark();
Output
This animal is eating.
The dog is barking.
Explanation
- In this code, we have an Animal class with an eat() method that shows a message.
- The dog class inherits from animals, so it can use the eat() method and has its own bark() method.
- In the main function, we create a Dog object, make it eat, and then make it bark to show both actions.
Polymorphism
- Polymorphism is like giving different objects the ability to respond to the same action in their own way.
- It allows you to use a single method name or interface, but each object can implement its own behavior.
- With polymorphism, you can write flexible code where different types of objects are handled through a common interface or method, yet each object can do something unique.
- This reduces complexity and makes it easier to extend or change parts of your program without affecting the whole system.
- When designing your classes, think about how objects with shared behaviors can respond differently to the same method.
- Good use of polymorphism keeps your code adaptable and easier to maintain, making your software more flexible and powerful.
Example
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() {
cout << "The animal makes a sound." << endl;
}
};
class Cat : public Animal {
public:
void sound() override {
cout << "The cat meows." << endl;
}
};
class Dog : public Animal {
public:
void sound() override {
cout << "The dog barks." << endl;
}
};
int main() {
Animal* myAnimal = new Animal();
Animal* myCat = new Cat();
Animal* myDog = new Dog();
myAnimal->sound();
myCat->sound();
myDog->sound();
delete myAnimal;
delete myCat;
delete myDog;
return 0;
}
using System;
class Animal
{
public virtual void Sound()
{
Console.WriteLine("The animal makes a sound.");
}
}
class Cat : Animal
{
public override void Sound()
{
Console.WriteLine("The cat meows.");
}
}
class Dog : Animal
{
public override void Sound()
{
Console.WriteLine("The dog barks.");
}
}
class Program
{
static void Main()
{
Animal myAnimal = new Animal();
Animal myCat = new Cat();
Animal myDog = new Dog();
myAnimal.Sound();
myCat.Sound();
myDog.Sound();
}
}
class Animal:
def sound(self):
print("The animal makes a sound.")
class Cat(Animal):
def sound(self):
print("The cat meows.")
class Dog(Animal):
def sound(self):
print("The dog barks.")
my_animal = Animal()
my_cat = Cat()
my_dog = Dog()
my_animal.sound()
my_cat.sound()
my_dog.sound()
abstract class Animal {
void sound() {
System.out.println("The animal makes a sound.");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("The cat meows.");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("The dog barks.");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal();
Animal myCat = new Cat();
Animal myDog = new Dog();
myAnimal.sound();
myCat.sound();
myDog.sound();
}
}
class Animal {
sound() {
console.log("The animal makes a sound.");
}
}
class Cat extends Animal {
sound() {
console.log("The cat meows.");
}
}
class Dog extends Animal {
sound() {
console.log("The dog barks.");
}
}
const myAnimal = new Animal();
const myCat = new Cat();
const myDog = new Dog();
myAnimal.sound();
myCat.sound();
myDog.sound();
Output
The animal makes a sound.
The cat meows.
The dog barks.
Explanation
- The
Animal
class has a virtual methodsound
, which provides a default message indicating that the animal makes a sound. - The
Cat
class inherits fromAnimal
and overrides thesound
method to output "The cat meows." - The
Dog
class also inherits fromAnimal
and overrides thesound
method to output "The dog barks." - In the
main
function, three pointers of typeAnimal
are created, each pointing to instances ofAnimal
,Cat
, andDog
. - When calling the
sound
method on each pointer, the program demonstrates polymorphism: the correct overridden method is executed based on the actual object type. - Finally, the program cleans up by releasing the dynamically allocated memory for each object using
delete
.
Dynamic Binding
- Dynamic binding is similar to making decisions about which method to run at the last moment during runtime.
- Instead of determining which technique will be utilized when the code is created, the decision is made when the program is executed.
- This provides greater versatility, particularly when dealing with inheritance and polymorphism.
- Dynamic binding allows the computer to select which method to call based on the object type rather than the reference type, guaranteeing that the proper version of a method is executed.
- This allows you to develop more flexible and reusable code, as objects can change behavior even when accessed using common references.
- When using dynamic binding, think about how methods can be overridden in derived classes to provide specific functionality while still sharing a common interface. It makes your code more adaptable and allows for smoother extensions and updates.
Message Passing
- Message passing allows things to communicate with one another by sending and receiving messages.
- It's similar to how people talk to each other to share information or ask for assistance.
- In programming, when one object needs to interact with another, it sends a message to request an action or share data.
- Think of it like sending a text message to a friend: you tell them what you want or need, and they answer accordingly.
- In object-oriented programming, message passing enables objects to request methods or properties from other objects, resulting in better-ordered and modular code.
For example, if you have a Car class and another called Driver, the Driver can send a message to the Car instructing it to start or stop. This way, the Driver does not need to understand how the Car operates internally. It simply sends a message to accomplish an action. This technique makes your code easier to comprehend and maintain because each object may focus on its own job while working with others.
Why is object-oriented programming required?
- Object-oriented programming (OOP) is important because it helps you organize code in a way that is easier to manage and understand.
- By using objects, you can create reusable code, which saves time and effort.
- OOP also makes it simpler to model real-world situations, allowing you to solve problems more intuitively.
- Additionally, it promotes better collaboration among developers and enhances code maintainability, making it easier to update and extend applications over time.
OOPs Interview Questions and Answers |
Summary
Object-Oriented Programming (OOP) helps you organize your code in a way that is easy to manage and understand. It is like building with reusable blocks, making it quicker to create and modify applications. OOP also helps you model real-world situations, allowing you to solve problems more naturally. Plus, it makes it easier for you to work with others and update your code over time. By using object-oriented programming (OOP) concepts, Scholarhat offers training programs such as C programming, CPP, C#, Java, and Python. You can enroll in any one of them and kickstart your career.