1. Introduction

We want to devote today's lesson to encapsulation. You already know what it is in general terms.

Encapsulation

What are the benefits of encapsulation? There are quite a few of them, but I can single out four that, in my opinion, are the main ones:


2. Valid internal state

In programs, situations often arise when an object interacts with several other classes. These interactions with the object can corrupt the data inside the object, making it impossible for the object to continue to work as expected.

As a result, the object needs to keep track of any changes to its internal data, or better yet, make the changes itself.

If we don't want some variable to be changed by other classes, then we declare it private. Once we do that, only the methods of its own class can access it. If we want the variables to be read-only, then we need to add a public getter for the relevant variables.

For example, suppose we want everyone to be able to know the number of elements in our collection, but we do not want them to be able to change the collection without our permission. Then we declare a private int count variable and a public getCount() method.

Proper use of encapsulation ensures that no class can directly access our class's internal data, which therefore prevents any changes beyond our control. These changes are only possible through calling the methods of the same class as the variables being changed.

It's best to assume that other programmers will always use your classes in the way that is most convenient for them, not the way that is safest for you (for your class). This behavior is the source of both bugs as well as attempts to prevent them.


3. Validating method arguments

Sometimes we need to validate the arguments passed to our methods. For example, say we have a class that represents a person and lets you set a birthdate. We must check all the input data to ensure it makes sense with the logic of the program and the logic of our class. For example, to disallow a birthdate in a 13th month or on February 30, and so on.

Why would someone indicate February 30 for their date of birth? First, this could be a user error when entering the data. Second, a program might have plenty of errors in it before it begins to run like clockwork. For example, the following situation is possible.

A programmer writes a program that identifies people whose birthday is the day after tomorrow. For example, let's say today is March 3. The program adds the number 2 to the current day of the month and looks for everyone who was born on March 5. It seems that everything is correct.

But when March 30 comes, the program will not find anyone, because the calendar has no March 32. A program has far fewer errors if we check the data passed to methods.

Remember when we studied ArrayList and analyzed its code? We saw that the get and set methods checked whether index is greater than or equal to zero and less than the length of the array. What's more, these methods throw an exception if the index falls outside the bounds of the array. This is a classic example of input validation.


4. Minimizing errors when changing code

Suppose we wrote a super useful class when we were involved in a large project. Everyone liked it so much that other programmers started using it in hundreds of places in their code.

The class was so useful that you decided to give it some improvements. But if you remove any methods from the class, then the code of dozens of people will stop compiling. They will have to rewrite everything. And the more changes you make, the more errors you will create. You will break lots of assemblies and you will be hated.

But when we change methods that are declared as private, we know that there is not a single other class anywhere that could be calling these methods. We can rewrite them, change the number of parameters and their types, and any dependent external code will continue to work. Well, at least it will compile.


5. We decide how our object interacts with external objects

We can restrict some of the actions that can be performed with our object. For example, suppose we want an object to be instantiated only once. Even if it may be created in several places in the project. And we can do this thanks to encapsulation.

Encapsulation 2

Encapsulation lets us add additional restrictions, which can be turned into additional advantages. For example, the String class is implemented as an immutable object. An object of the String class is immutable from the moment of its creation until the moment of its death. All the methods of the String class (remove, substring, ...), return a new string without making any changes to the object on which they are called.

Encapsulation is a very interesting thing.