What is Encapsulation in OOPS?

Encapsulation is one of the fundamental concepts in OOP that bundles data and associated methods that operate on that data into a single block called class. It is also seen as a pathway for restricting direct access to some data and methods associated with a class (which leads to data hiding). In other words: Encapsulation is about wrapping data and methods into a single class and protecting it from outside intervention. 

The word “Encapsulation” can be thought of as a derived form of the word encapsulate where we encapsulate data members (variables) and associated functions (methods) in a class. In the famous book Object-Oriented Analysis and Design, Grady Booch writes: "Encapsulation is the process of compartmentalizing the elements of an abstraction that constitute its structure and behavior; encapsulation serves to separate the contractual interface of an abstraction and its implementation".

What do you mean by encapsulation?

This definition might not be clear to some of you, specifically those unaware of abstraction. The fact is that many programmers find encapsulation and abstraction as similar concepts, which is not entirely correct. No worries! We’ll see abstraction at a later point and try to understand the fine line between both.

Up to this point, we might think that what is so special about encapsulation or how all this idea makes sense? Well, think about the following 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 seen that encapsulation ensures the bundling of data and related stuff with the motive of data hiding and providing methods for smooth communication. Now let’s try to see it through an example.

Think of a car (any model will work). Now think about the components of the car. We might think of tires, steering wheel, engine, shafts, etc. These all are nothing but data members of Car (if Car is a class, then these are the variables). Now you might think of the methods. It’s simple, the methods of this car are moving, steering, honking, etc. To summarize the example, the Car represents the class; the components are data variables, and the actions of the car are the methods.

We might be able to see encapsulation here. Various components (the variables) are bundled in the Car (the class) along with many functions which use those components (methods). We can also see that this encapsulation is helping in abstraction. For instance, you can’t see the engine or shafts or the entire driving mechanism, but you are driving the car comfortably via some interfaces. This is the prime feature of abstraction, where the client doesn’t care about the internal mechanism or the internal representation of data but only cares about completing a task using the interface (often provided by the methods).

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 but now with code:

class employee
{
    //info of the employee
private:                 
        int ID;
        string name;
        int joinYear;
    
public:
    int getId()
    {
        return ID;
    }
    string getName()
    {
        return name;
    }
    int getYear()
    {
        return joinYear;
    }
    
    void setId(int newID)
    {
        ID = newID
    }
    
    void setName(string newName)
    {
        name = newName
    }
    
    void setYear(int newYear)
    {
        joinYear = newYear
    }
};

Code explanation

We’ve defined an employee class that has 3 data members: ID, name, and joinyear; and has 6 methods: getid(), getname(), getyear(), setid(), setname() and setyear().

How this code ensures encapsulation?: We bundled all details of an employee under employee class (Bundling of data). We also restricted direct access to data members by using private access specifier (Data hiding).

Example Code: Encapsulation in Java

class Student
{
    //info of the student
    private String studentName;
    private int studentRollNumber;
    private int studentAge;

    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 the 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 the details of the implementation.

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

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

Types of Encapsulation in OOPs

There are three basic techniques to encapsulate data in Object Programming. Data members, methods, and classes can all be encapsulated.

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. The 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 languages provide access modifiers to control the visibility and accessibility of class-level structures and hide sensitive data from users. Programmers should use these access modifiers to differentiate between objects’ public and non-public interfaces.

Access modifiers in a class 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 from other classes and functions. 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 the access level is limited to the same class or any subclass inherited from that class. This access through inheritance can alter the access modifier of the base class elements in the derived class depending on the modes 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 by default. 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 abstraction a bit.

Briefly, abstraction is the solution for complex systems. In abstraction, we focus on the outside view of an object; therefore, it separates an object’s 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.

Now that we have a basic idea about both concepts. Let’s see how they are different from each other.

  • Abstraction focuses upon the observable behavior of an object, whereas encapsulation focuses upon the implementation that gives rise to this behavior (the observable behavior).
  • Abstraction is the process of gaining information. In contrast, encapsulation is the process of containing the information.
  • In abstraction, problems are solved at the interface level. While in encapsulation, problems are solved at the implementation level.
  • Abstraction is the method of hiding unwanted information. At the same time, encapsulation is a method to hide the data in a single entity and protect information from outside.
  • We can implement abstraction using abstract classes and interfaces. Whereas encapsulation can be implemented using by access modifier i.e. private, protected, and public.
  • In abstraction, implementation complexities are hidden using abstract classes and interfaces. While in encapsulation, the data is hidden using getters and setters methods.
  • Encapsulation provides explicit barriers among different abstractions and thus leads to a 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 (the interface) and encapsulation is a methodology that helps the programmer create this interface!

Still not sure? Let’s see a quick example. 

Think of the keyboard of your pc/laptop. From your perspective, you are pressing the keys. But each key acts as a mechanical switch that closes an electrical circuit when pushed. The computer further comprehends this active circuit to give the desired result. This comprehension part is irrelevant to a general user; that’s why it is abstracted. Moreover, all these circuits and comprehension mechanisms are encapsulated under your keyboard (or somewhere 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 design a subclass and inherit implementation from a base class. Then any small changes in the implementation of the base class will ripple through the class hierarchy. This rippling will affect all the sub-classes. So by using inheritance, encapsulation gets weakened within a class hierarchy. In other words: inheritance indicates strong encapsulation with other classes but weak encapsulation between a base class and its derived classes.

  • To reduce the risk, we should stick to the strict is-a relationship when using inheritance. If the sub-classes are genuinely related to the base class, changes to the base class would naturally affect the derived class.
  • Encapsulation is a basic OOPS idea, so instead of making all attributes public, we can try to make all attributes private. But it will limit the use of inheritance because if a subclass does not have access to the attributes of its base class, this situation can lead to a critical design challenge. So based on the need for an inheritance, we can allow subclasses to access the base class attributes using the access modifier protected.

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

  • Inheritance is the process or mechanism by which you can acquire the 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

  • Increase code reusability: Think of various objects of a single class. To declare hundreds of objects, we had to define the class only once with the help of encapsulation. It increases code reusability by providing public well-defined service methods so that the role of the object becomes clear to other objects. In other words, having well-defined interfaces from a software development perspective allows other programmers to understand our code and reuse it in other applications quickly.
  • Increasing robustness: Encapsulation reduces system complexity, and thus increases robustness, by allowing programmers to limit the interdependencies between software components.
  • Better code maintenance: using encapsulation, code changes can be made independently without affecting other classes. In other words, the method is described in a single location rather than multiple locations. We simply need to know what outcome a method will produce and when to use it.
  • Data security and Information hiding: Only the object’s own methods can directly inspect or manipulate its data. So encapsulation hides the internals of the object and protects its integrity by preventing users from setting the internal data of the component into an invalid or inconsistent state. So it ensures the security of class data (or access of the data outside of the class) and methods by using proper access modifiers.
  • Improves code clarity and comprehensibility: Looking at a whole program, we may have many objects, each exposing a simple interface to the other objects and keeping the details of its own implementation hidden. With all the objects following this strategy, we escape the complex trap of writing and debugging large programs. In simple words, encapsulation helps us in unit-testing and debugging a code. The programmer will less likely waste time in debugging because all inter-related data members and methods are in one section of the code (a class), and the rest of the code is irrelevant to that class.

Enjoy learning, Enjoy algorithms, Enjoy OOPS!

Share feedback with us

More blogs to explore

Our weekly newsletter

Subscribe to get weekly content on data structure and algorithms, machine learning, system design and oops.

© 2022 Code Algorithms Pvt. Ltd.

All rights reserved.