Talents en applications - Blogue | Nexapp

Increase encapsulation by moving code out of your classes

Written by Martin Boucher | May 21, 2024 3:24:10 PM

In this article, I propose a trick to increase the encapsulation of your code. I use it systematically. It has the advantage of being applied without thinking (or almost 😊). Decision-making is simple.

Definition of encapsulation

Encapsulation refers to the grouping of data with the mechanisms that operate on it. Encapsulation provides two main advantages:

  • Data access control: An object's data is hidden outside the class and can only be modified by specific methods of that class (usually called accessor methods or accessors). This helps maintain data integrity by avoiding uncontrolled modifications.

  • Modularity: Encapsulation separates the details of a class's implementation from its use. This means that code that uses a class does not need to understand how that class is implemented. This makes it easier to maintain and evolve the code.

An example of encapsulation

I play the game Arkham Horror: The Card Game. This deck-building game requires purchasing packs of playing cards to build a collection. I would love to have an application to manage my card collection (obviously, I prefer programming it to using Excel 😉).

Fortunately, there is this website, but above all, their API. In the following example, I have a repository to get a card from this API:

I have this repository, which fetches from the API on lines 14 to 16 and then delegates to a data mapper toCard on line 17 to transform the data received from the server into an object I need for my application.

Have you noticed that this data mapper toCard does not use this?

Even though this data mapper has strong cohesion with the data received from the server, it has very little coupling with the class. It can be extracted to a function. Let's try this!

In terms of behavior during execution, it’s all the same: we fetch from the server; we transform the data.

For the public interface of this module, the data mapper is just as hidden as before. It is not exposed to repository users.

So what do we gain? Encapsulation.

How can extracting toCard data mapper outside the class increase encapsulation?

Let’s first review data access control. Restricting access to the data improves encapsulation. This is the case here, and it was easy because the data was not used. Why let the data mapper toCard have access to data it doesn’t need?

It is like having useless keys on your keyring.

What about modularity? The repository only uses the data mapper. It doesn’t have to implement it, and the data mapper doesn’t use the HTTP client. With this refactoring, we got two linked but independent pieces of code. Each of them can evolve without necessarily impacting the other.

Encapsulation is even more valuable

As developers, we read a lot more code than we write it (90% reading and 10% writing reminds me of my colleague Alexandre Rivest in this article). It is of the utmost importance that our interpretation of it is correct.

After all, it's communication.

Let’s represent the two situations using diagrams:

On the left, we have the data mapper in the class; on the right, the data mapper is extracted.

Why is there an intersection between the HTTP client and the data mapper in the class?

What I want to illustrate with this intersection is the fogginess, this possibility that there is a connection until we have taken time to dig into the details.

We navigate through the code as we do on an interactive map (e.g.: Google Map): The higher we hover, the less we see the details. We don't want to see them anyway. We want to be able to find our way without having to explore every corner.

On the left side, we hover over a class. We see two methods: the class has a member. What methods use this member? What do they do with this member? How do these methods work together?

These are all questions we unconsciously ask ourselves when analyzing code.

On the right side, we hover over a class and its method. A private function is available in the module. We already know that the class does not have access to the function implementation, and the function does not have access to the class implementation.

The isolation is explicit.

Conclusion

When you see this method making no use of this, you need to extract it from the class. By doing so, you increase encapsulation since you will get two smaller and more cohesive capsules.

The capsules obtained can evolve independently without affecting each other. They can be moved to other modules or packages more easily.

This isolation will be exposed explicitly. The code will be more understandable without having to scrutinize it.