1. Overview

Encapsulation is an essential object-oriented programming paradigm. It allows data and methods to be grouped together in a class. However, encapsulation itself doesn’t guarantee defensive programming.

To achieve robustness, we employ information hiding. Information hiding is a programming principle that promotes the idea of restricting access to internal implementation details.

In this tutorial, we’ll explore details about encapsulation and information hiding. Additionally, we’ll see some sample code and understand the key differences between the two concepts.

2. Historical Background

The term “information hiding” was coined by Parnas in 1972 in an attempt to differentiate procedural programming from modular programming.

Parnas infers that the implementation of data should be unknown to an outside module.

Furthermore, in 1973, Zelis came up with the word encapsulation to explain how to reduce access to underlying data in a class to prevent unwanted modification.

In 1978, Parnas claimed the words encapsulation and information hiding are synonymous. However, in 2001, Paul Roger showed the difference between encapsulation and information hiding with sample code. He claimed we can have encapsulation without information hiding.

Encapsulation is a broad concept in object-oriented programming that bundles data with methods. This ensures a modular design.

Encapsulation and information hiding are sometimes used synonymously, but there are some differences.

3. Encapsulation

According to Paul Roger, encapsulation is simply bundling data with operations that act on the data.

3.1. Without Encapsulation

Let’s see an example class without encapsulation:

class Book {
    public String author;
    public int isbn;
}

Here, the Book class has public access modifiers for its fields. This makes the fields accessible from external classes.

Let’s create another class named BookDetails to get the details of a Book object:

class BookDetails {
    public String bookDetails(Book book) {
        return "author name: " + book.author + " ISBN: " + book.isbn;
    }
}

The BookDetails class contains a method that uses Book data. Here, we treat the Book class as a container for data and BookDetails as a collection of methods that act on the data.

We use a programming procedure that enforces creating an implementation class that operates on data from another class. This programming style is archaic, and it’s not modular.

Here’s a unit test to get the details of a book:

@Test
void givenUnencapsulatedClass_whenImplementationClassIsSeparate_thenReturnResult() {
    Book myBook = new Book();
    myBook.author = "J.K Rowlings";
    myBook.isbn = 67890;
    BookDetails details = new BookDetails();
    String result = details.bookDetails(myBook);
    assertEquals("author name: " + myBook.author + " ISBN: " + myBook.isbn, result);
}

The implementation above can be simplified with encapsulation. Changes in Book class affect any classes that use Book data.

3.2. With Encapsulation

Let’s improve the design in the last section using encapsulation. We can bundle the data and the methods operating on it into a single class:

class BookEncapsulation {
    public String author;
    public int isbn;
    public BookEncapsulation(String author, int isbn) {
        this.author = author;
        this.isbn = isbn;
    }
    public String getBookDetails() {
        return "author name: " + author + " ISBN: " + isbn;
    }
}

Here, we improve the code by bundling the implementation with the class. This makes the code modular. Clients can easily invoke the getBookDetails() methods without having much knowledge of its implementation.

Let’s see a unit test to invoke getBookDetails():

@Test
void givenEncapsulatedClass_whenDataIsNotHidden_thenReturnResult() {
    BookEncapsulation myBook = new BookEncapsulation("J.K Rowlings", 67890);
    String result = myBook.getBookDetails();
    assertEquals("author name: " + myBook.author + " ISBN: " + myBook.isbn, result);
}

Encapsulation improves the code, but external classes can modify the Book data because the fields use a public access modifier. However, encapsulation doesn’t have strict rules on which access modifier to use. We can use the public, private,  and protected access modifiers.

Additionally, encapsulation helps introduce a feature to a class without breaking external code using the class. Let’s modify the BookEncapsulation class by introducing an id:

// ...
public int id = 1;
public BookEncapsulation(String author, int isbn) {
    this.author = author;
    this.isbn = isbn;
}
public String getBookDetails() {
    return "author id: " + id + " author name: " + author + " ISBN: " + isbn;
} 
// ...

Here, we introduce the id field and modify the return details of getBookDetails(). These internal changes won’t break any client code. This makes encapsulation powerful and modular.

However, we can make encapsulation more strict by applying the idea of “information hiding”. Encapsulation isn’t enough for defensive programming. We need to apply information hiding with encapsulation to prevent unwanted data modification.

4. Information Hiding

Information hiding is a programming principle that aims to prevent the direct modification of the data of a class. Also, it provides a strict guideline to access and modify the data of a class.

Additionally, it helps to hide design implementations from the client, especially design implementations that are likely to change.

Furthermore, information hiding when used with encapsulation ensures modular code.

Let’s improve the encapsulated class by applying information hiding:

class BookInformationHiding {
    private String author;
    private int isbn;
    private int id = 1;
    
    public BookInformationHiding(String author, int isbn) {
        setAuthor(author);
        setIsbn(isbn);
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }
    public int getIsbn() {
        return isbn;
    }
    public void setIsbn(int isbn) {
        this.isbn = isbn;
    }
    public String getBookDetails() {
        return "author id: " + id + " author name: " + author + " ISBN: " + isbn;
    }
}

Here, we use the private access modifier to restrict access and altering of fields from an external class. Furthermore, we create getters and setters to set how the field can be accessed and modified.

In contrast to encapsulation without information hiding, where any access modifier can be used, information hiding is implemented through the use of a private access modifier. This restricts access to the fields within the class only.

The fields cannot be directly accessed from a client code, thus making it more robust.

Let’s see a unit test for the modified class:

@Test
void givenEncapsulatedClass_whenDataIsHidden_thenReturnResult() {
    BookInformationHiding myBook = new BookInformationHiding("J.K Rowlings", 67890);
    String result = myBook.getBookDetails();
    assertEquals("author id: " + 1 + " author name: " + myBook.getAuthor() + " ISBN: " + myBook.getIsbn(), result);
}

Additionally, we can create a strict rule on how data can be modified. For instance, we can avoid a negative ISBN by modifying the setter:

public void setIsbn(int isbn) {
    if (isbn < 0) {
        throw new IllegalArgumentException("ISBN can't be negative");
    }
    this.isbn = isbn;
}

Here, we create a strict rule on how to modify the ISBN.

5. Key Differences

Here’s a summary table showing the key differences between encapsulation and information hiding:

Information Hiding

Encapsulation

A design principle to hide implementation details and unintended modification to data

An object-oriented principle that bundles data with functions operating on them.

Strictly uses the private access modifier

Not strict on access modifiers and can use a public, private, or protected access modifier

Helps to achieve defensive programming

A methodology to achieve information-hiding

6. Conclusion

In this article, we learned the key concepts of encapsulation and information hiding. Additionally, we saw an example code that shows the slight differences between encapsulation and information hiding.

However, a school of thought claims information hiding and encapsulation are synonymous. We need to always apply the principles of information hiding when encapsulating a class.

As always, the source code for the examples is available over on GitHub.