What is Encapsulation in OOPS?

Encapsulation is a fundamental concept in object-oriented programming (OOP) that involves bundling data and the methods that operate on that data within a single unit, known as a class. This concept helps to protect the data and methods from outside interference, as it restricts direct access to them. In other words, encapsulation involves wrapping data and methods within a class to create a protective barrier around them.

In the book "Object-Oriented Analysis and Design," Grady Booch defines encapsulation as "the process of compartmentalizing the elements of an abstraction that constitute its structure and behavior; encapsulation separates the contractual interface of an abstraction and its implementation." In other words, encapsulation helps to separate the interface (public-facing aspect of a class) from the implementation (internal workings of the class). This allows for flexibility in the design of a class, as the implementation can be modified without affecting the interface.

What do you mean by encapsulation?

If this definition of encapsulation is not clear to you, it may be because you are not familiar with the concept of abstraction. It is common for programmers to confuse encapsulation and abstraction, as they are related concepts. However, they are distinct ideas and it is important to understand the differences between them. We have covered this difference in the last section of this blog.

Up to this point, we might think that what is so special about encapsulation or how all this idea makes sense? Well, think about these lines: The true value of encapsulation is recognised in an environment that is prone to change. If our code is well-encapsulated, we can better manage risk in the event of a requirement change. By encapsulating the code, we are less likely to change insignificant part (for us) of the code.

Example of encapsulation in real life

We've learned that encapsulation involves bundling data and related methods in order to protect data and facilitate communication. Let's look at an example to further illustrate this concept.

Imagine a car (any model will work). Now, think about the various components that make up the car, such as tires, wheel, engine, and shafts. These components can be thought of as data attributes of the class "Car." Now, consider actions that a car can perform, such as moving, steering, honking, etc. These actions can be thought of as the methods of the "Car" class.

In the above example, we can see how encapsulation is used to group together data attributes within the "Car" class, along with the methods that operate on those components. This bundling helps to abstract the inner workings of the car from the user. For example, we don't need to know how the engine or shafts work in order to drive the car.

We simply use the interface provided by the methods (such as steering and moving) to complete the task. This is the essence of abstraction, where client is only concerned about the interface to achieve a specific goal, without worrying about the internal mechanism. So encapsulation and abstraction work together to create a clear separation between the interface and implementation of a class, making it easier to understand and maintain the code.

Encapsulation in real life example 1

Encapsulation in oops with real time example

Example code: Encapsulation in C++

Let’s see another real-life example with code:

class Employee
{
private:                 
        int ID;
        string employeeName;
        int joinYear;
    
public:
    
    Employee(int id, string name, int year)
    {
        ID = id;
        employeeName = name;
        joinYear = year;
    }
    
    int getId()
    {
        return ID;
    }
    
    string getName()
    {
        return employeeName;
    }
    
    int getYear()
    {
        return joinYear;
    }
    
    void setId(int newID)
    {
        ID = newID
    }
    
    void setName(string newName)
    {
        employeeName = newName
    }
    
    void setYear(int newYear)
    {
        joinYear = newYear
    }
};

In this example, "Employee" class has been defined with three data members (ID, name, and joinyear) and six methods (getId(), getName(), getYear(), setId(), setName(), and setYear()). This code demonstrates encapsulation in several ways:

  • Data bundling: All details related to an employee are bundled together within the "Employee" class.
  • Data hiding: Data members are marked as private, which means they can only be accessed directly within the class. This help us to protect the data from outside interference.

Example code: Encapsulation in Java

class Student
{
    private String studentName;
    private int studentRollNumber;
    private int studentAge;
    public Student(String name, int rollNumber, int age)
    {
        studentName = name;
        studentRollNumber = rollNumber;
        studentAge = age;
    }

    public int getAge() 
    { 
        return studentAge; 
    }

    public String getName() 
    { 
        return studentName; 
    }
 
    public int getRollNumber() 
    { 
        return studentRollNumber; 
    }
 
    public void setAge(int newAge) 
    { 
        studentAge = newAge; 
    }
 
    public void setName(String newName)
    {
        studentName = newName;
    }

    public void setRoll(int newRoll) 
    { 
        studentRollNumber = newRoll; 
    }
}

Let’s understand encapsulation from another perspective!

Each object encapsulates some data and methods. The object takes requests from other client objects without exposing details of its data or methods. The object alone is responsible for its own state, declaring private data and methods for the implementation and exposing the public interface for clients. The client depends on the public interface and does not depend on details of the implementation.

  • The idea here is to hide implementation complexity inside an object and keep various objects as independent from each other as possible.
  • Ideally, interface is exposed in a way that is simple for other objects to understand, because for most problems, clients don’t really care about implementation details. So an interface can capture just features relevant to the client, which is much simpler than the full implementation.
  • Constructors provide a good mechanism to support encapsulation. By designing proper constructors, we can properly initialize our encapsulated data.
  • To access values, class usually provides publicly accessible methods (getters and setters), which other client classes call to retrieve and modify the values within the object. Within a class, getter method is a method that allows user to access values stored in data members, whereas setter method allows user to set values of data members.

Note: To experience the power of encapsulation, one should know getter and setter methods. By only using getter methods in a class, one can make a read-only class. Similarly, by only using setter methods, one can make a write-only class.

Types of Encapsulation in OOPs

There are three basic techniques to implement encapsulation in object oriented programming.

Data Member Encapsulation: Data members can be defined as private members of the Class. Setters and Getters methods should be used by any object that wants to change or retrieve the value of a data member. 

Method Encapsulation: We can hide methods used for internal implementation that does not need to be visible to the public. Such method should be declared as private so that the user does not have access to it. 

Class Encapsulation: Our implementation might contain an internal implementation of a class that stores and processes specific information. We encapsulate that class by defining it as private and hiding it from user access. These classes should not be included in any public interface.

How to Hide Information via Encapsulation?

Object-oriented programming provide access modifiers to control visibility and accessibility of class-level structures and hide sensitive data from users. Programmers should use these access modifiers to differentiate between public and non-public interface of an object.

Access modifiers are used to restrict the scope of a class, constructor, variable, method, or data member. It sets some restrictions on the class members not to get directly accessed by the outside functions. In object-oriented programming, there are four different types of access modifiers:

Public

The public access modifier has a broad scope. It implies that public class members (classes, methods, or data members) can be accessed using object of other classes. In other words, public class members have no restriction on the scope, and they can be accessible from everywhere in the program.

Private

The class members declared private are limited to the scope of the class and can be accessed only by the member methods inside the class. In other words, they can not be accessed directly by any object or method outside the class.

Protected

A protected access modifier is similar to a private access modifier, but access level is limited to the same class or any subclass inherited from that class. This access through inheritance can access base class elements in the derived class depending on the mode of inheritance.

Default

When no access modifier is specified for a class, method, or data member, it is said to have the default access modifier. In other words, the default members’ access is limited to the current or the same package. Classes not in the same package cannot access or use the default members.

Hiding Information via access modifiers to achieve Encapsulation

Encapsulation vs Abstraction

To understand the difference, we need to understand the idea of abstraction. In abstraction, we focus on the outside view of an object and separate essential behavior from its implementation. To encapsulate abstraction, here’s an extract from the same book of Grady Booch: "An abstraction denotes the essential characteristics of an object that distinguish it from all other kinds of objects and thus provide crisply defined conceptual boundaries, relative to the perspective of the viewer."

Let’s see how they are different from each other.

  • Abstraction is the process of exposing observable behavior of an object, while encapsulation is the process of hiding implementation that gives rise to that behavior.
  • Abstraction addresses problems at the interface level, while encapsulation addresses them at the implementation level.
  • Abstraction involves hiding unwanted information, while encapsulation involves protecting information from the outside by enclosing it within a single entity.
  • Abstraction can be implemented using abstract classes or interfaces, while encapsulation can be implemented using access modifiers like private, protected, and public.
  • Encapsulation creates explicit barriers between different abstractions and promotes clear separation of concerns.

After summing up the differences, one can say that an abstraction consists of how an object and its behaviors are presented to the user (interface) and encapsulation is a methodology that helps the programmer create this interface!

For example, consider the keyboard on your computer. When you press a key, it acts as a mechanical switch that closes an electrical circuit. The computer then processes this active circuit to produce the desired result. The details of this processing are not relevant to the general user and are therefore abstracted. Additionally, all circuits and processing mechanisms are encapsulated within the keyboard or elsewhere in the machine.

This beauty of encapsulation helps achieve abstraction and makes complex implementations look as easy as pressing a key.

Encapsulation vs inheritance

As we already know, encapsulation is about designing classes for both private implementation and public interface. But what does encapsulation have to do with an inheritance? How does inheritance break the principle of encapsulation?

Suppose we create a subclass and inherit implementation from a base class. Any changes made to the base class's implementation will affect all subclasses in the class hierarchy. This is because inheritance creates a strong connection between classes, but weakens encapsulation within a class hierarchy. In other words, inheritance is a mechanism for strong encapsulation between different classes, but weak encapsulation between a base class and its derived classes.

  • To minimize risk, it is best to follow a strict "is-a" relationship when using inheritance. If subclass is truly related to the base class, changes to the base class will naturally affect the subclass.
  • Encapsulation involves making attributes private rather than public. However, this can limit the use of inheritance, as a subclass may not have access to the attributes of its base class. To solve this design challenge, the protected access modifier can be used to allow subclasses to access the attributes of their base class when necessary. This allows for a balance between encapsulation and use of inheritance.

So here is the summary of the comparison between inheritance and abstraction:

  • Inheritance is the process by which we can acquire properties and behavior of a class into another class. Encapsulation refers to data winding into a single unit known as class.
  • Inheritance indicates that a subclass inherits attributes and methods from a base class. Encapsulation suggests that one class must not have access to another class’s (private) data.

Advantages of Encapsulation

Encapsulation can provide several benefits to a software system:

  • Increased code reusability: Encapsulation allows us to define a class once and create multiple objects from that class. By providing public, well-defined methods that act as the object's interface, we can make it easier for other programmers to understand object's role and reuse object's code in other applications.
  • Increased robustness: Encapsulation reduces system complexity by allowing programmers to limit interdependencies between software components. This can help to increase robustness of the system by reducing number of potential points of failure.
  • Improved code maintenance: Encapsulation allows us to make code changes independently without affecting other classes. This can make it easier to maintain and update code over time.
  • Data security and information hiding: By making an object's data members and methods private, we can protect object's internal state and ensure that object is used in a consistent and predictable way. This can help to improve security of object's data and prevent it from being accessed or modified in an unauthorized way.
  • Improved code clarity and comprehensibility: Encapsulation helps to keep related data members and methods together in a single class, making it easier to understand and work with the code. This can help to improve the clarity and comprehensibility of the code, making it easier to unit-test and debug.

Enjoy learning, Enjoy algorithms, Enjoy OOPS!

More from EnjoyAlgorithms

Self-paced Courses and Blogs