C# tutorials > Core C# Fundamentals > Object-Oriented Programming (OOP) > What are the four pillars of OOP?
What are the four pillars of OOP?
Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects", which contain data in the form of fields (often known as attributes or properties) and code, in the form of procedures (often known as methods). The core principles of OOP are built upon four fundamental pillars. Understanding these pillars is crucial for designing robust, maintainable, and scalable software.
The Four Pillars of OOP: An Overview
The four pillars of Object-Oriented Programming are:
Abstraction: Hiding Complexity
Description: Abstraction is the process of simplifying complex reality by modeling classes appropriate to the problem, and working at the appropriate level of inheritance. It focuses on what an object does rather than how it does it. Benefits:
Abstraction: Example
Explanation: In this example, IShape
is an abstract representation of a shape. We only define the Area()
method, hiding the specific details of how the area is calculated for different shapes. The Circle
and Rectangle
classes implement the IShape
interface and provide their own implementation of the Area()
method.
public interface IShape
{
double Area();
}
public class Circle : IShape
{
public double Radius { get; set; }
public double Area() => Math.PI * Radius * Radius;
}
public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public double Area() => Width * Height;
}
Encapsulation: Bundling Data and Methods
Description: Encapsulation is the bundling of data (attributes) and methods that operate on that data into a single unit, known as a class. It controls access to the internal state of an object, preventing direct manipulation from outside the class. Benefits:
Encapsulation: Example
Explanation: In this example, the balance
attribute is declared as private
, meaning it can only be accessed from within the BankAccount
class. The Deposit
, Withdraw
, and GetBalance
methods provide controlled access to the balance. This prevents direct manipulation of the balance from outside the class and allows for validation logic within the methods.
public class BankAccount
{
private double balance;
public BankAccount(double initialBalance)
{
balance = initialBalance;
}
public void Deposit(double amount)
{
if (amount > 0)
{
balance += amount;
}
}
public void Withdraw(double amount)
{
if (amount > 0 && amount <= balance)
{
balance -= amount;
}
}
public double GetBalance()
{
return balance;
}
}
Inheritance: Creating New Classes from Existing Ones
Description: Inheritance is a mechanism by which a new class (derived class) inherits properties and behaviors from an existing class (base class). The derived class can then extend or modify the inherited properties and behaviors as needed. Benefits:
Inheritance: Example
Explanation: In this example, Animal
is the base class, and Dog
and Cat
are derived classes. The Dog
and Cat
classes inherit the Name
property from the Animal
class. They also override the MakeSound()
method to provide their own specific implementations. The virtual
keyword in the base class allows derived classes to override the method. The override
keyword in the derived classes indicates that the method is overriding a base class method.
public class Animal
{
public string Name { get; set; }
public virtual string MakeSound()
{
return "Generic animal sound";
}
}
public class Dog : Animal
{
public override string MakeSound()
{
return "Woof!";
}
}
public class Cat : Animal
{
public override string MakeSound()
{
return "Meow!";
}
}
Polymorphism: Many Forms
Description: Polymorphism is the ability of an object to take on many forms. It allows objects of different classes to be treated as objects of a common type. There are two main types of polymorphism: compile-time polymorphism (method overloading) and runtime polymorphism (method overriding). Benefits:
Polymorphism: Example
Explanation: In this example, both Circle
and Rectangle
implement the IShape
interface. The ShapeCalculator
class can calculate the total area of a list of IShape
objects, regardless of their specific type. This is polymorphism in action – treating objects of different classes as objects of a common type (IShape
). The CalculateTotalArea
method doesn't need to know the specific type of each shape; it only needs to know that it implements the Area()
method.
public interface IShape
{
double Area();
}
public class Circle : IShape
{
public double Radius { get; set; }
public double Area() => Math.PI * Radius * Radius;
}
public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public double Area() => Width * Height;
}
public class ShapeCalculator
{
public double CalculateTotalArea(List<IShape> shapes)
{
double totalArea = 0;
foreach (var shape in shapes)
{
totalArea += shape.Area();
}
return totalArea;
}
}
Real-Life Use Case Section
Consider a software system for a hospital. Abstraction is used to represent patients, doctors, and appointments without exposing the underlying database structure. Encapsulation protects patient medical records from unauthorized access. Inheritance allows creating specialized doctor classes (e.g., Surgeon, Cardiologist) from a general Doctor class. Polymorphism allows treating different types of appointments (e.g., check-up, surgery) in a uniform way within a scheduling system.
Best Practices
Interview Tip
When discussing OOP pillars in an interview, provide concrete examples from your own projects to demonstrate your understanding. Be prepared to discuss the advantages and disadvantages of each pillar, and how they contribute to good software design.
When to use them
Use the principles of OOP when you need to model complex systems with interacting objects. OOP is particularly well-suited for applications with a clear structure, reusable components, and a need for maintainability and extensibility.
Memory Footprint
OOP can introduce some overhead due to the creation of objects and the runtime resolution of polymorphic calls. However, the benefits of OOP, such as code reuse and maintainability, often outweigh the performance cost. Memory usage depends on the complexity of the objects and the number of objects created.
Alternatives
Alternatives to OOP include:
Pros
Cons
FAQ
-
What is the difference between abstraction and encapsulation?
Abstraction focuses on hiding complexity, showing only what's essential. Encapsulation focuses on bundling data and methods, controlling access and protecting data integrity. Abstraction deals with what an object does, while encapsulation deals with how it does it.
-
Why is inheritance important in OOP?
Inheritance promotes code reuse by allowing new classes to inherit properties and behaviors from existing classes. It also helps organize code by creating a hierarchical relationship between classes, making the system easier to understand and maintain.
-
How does polymorphism improve code flexibility?
Polymorphism allows objects of different classes to be treated as objects of a common type. This enables writing generic code that can work with multiple types without knowing their specific implementations. It also makes it easy to add new types to the system without modifying existing code.