We initially start with encapsulation at a very basic level — that of a class. We then gradually shift focus to other levels, such as components and web services.
You will learn
- What does Encapsulation mean?
- What are the advantages of building encapsulated software?
- Is encapsulation restricted to Object Oriented Design?
- How can encapsulation be followed in other programming approaches?
Encapsulation At The Basic Level (Level-1)
Let’s look at two implementations of a CricketScorer class:
If you’re not familiar the sport of cricket, you could imagine a FootballScorer class instead. Here, CricketScorer provides its users with two methods, setScore() and getScore(). These form the class interface.
Let’s next make use of this class:
The user here gets to call both getScore() and setScore(). Let’s say a boundary is scored in the cricket match, which should result in the score getting incremented by 4. The user is responsible for retrieving the current the current score, incrementing it by 4, and then setting it.
There are a few important things to note here:
- The score is declared private, which means users of CricketScorer cannot access it directly. We are hiding the implementation of “`score“ from the class users. This is what encapsulation is all about.
- The various Java language keywords such as private and protected are the most basic level of implementing encapsulation.
Method-Level Encapsulation (Level-2)
To avoid putting the onus of getting and setting the score on the user, we could define a method such as:
All the user does is call this four() method, and no longer needs to explicitly manipulate the stored score. Here we have introduced a four() method to encapsulate the process of getting and setting the score. This is the next level of encapsulation, at the logical level. The user now no longer needs to know how to manipulate the score.
Abstraction And Encapsulation
Abstraction is all about hiding complexity. How do you hide that complexity? By using Encapsulation. Apart from the two levels we saw just now, there are more ways to go with encapsulation. Let’s look at them next.
Encapsulation At The Class Level
The Factory Pattern
Have a look at the following example of code.
Suppose we have a set fo class to represent and create different kinds of persons. Also, we would need a way to do the same in code as well. Also, we would not like the users of the class not to know, that there are underlying implementations of Person named Male and Female.
We can abstract that detail away by using the Factory Pattern. As we see in the code above, the user creates a male person by passing in a name and a string “M” to the getPerson() method of PersonFactory. He has no idea that there are classes called Male and Female hidden away from him.
The Strategy Pattern
This is another good example of encapsulation. Consider the following piece of code:
In this case, we created an interface called Sortable. Any class that needs sorting behavior, such as:
The user of the interface, ComplexClass, only directly calls the sort() method on the Sortable interface. It does not worry about what the underlying class that implements Sortbale, actually is. It does not worry about whether that is a BubbleSort:
or a QuickSort. Whoever uses the class ComplexClass, needs to create an instance of it, provide an instance of the sorting algorithm, and out class can make use of sort()by accessing the interface.
An Interface Example
We are very much in the habit of playing games, especially those that involve using gaming consoles. How would a computer game allow you to play it? By allowing the console to use it like this:
This shows use of one such console with four buttons: up, down, right and left. We are representing it by an interface, named GamingConsole:
Whoever uses a GamingConsole, can play a game attached to it, irrespective of what the actual game is. It could be a MarioGame, a ChessGame, or whatever. The user knows he can call the up(), down(), left() and right() operations with ease. The actual game implementations are hidden away. How a MarioGame reacts to the console, is different from how a ChessGame responds.
The fact that we have created a GamingConsole interface, means the Gameimplementations can be hidden away.
An Abstract Class Example
The same high-level concept can be applied by using abstract classes as well. Consider the following abstract class definition:
We are implementing a Template Method Pattern in here. For every recipe that we end up creating, we want to make sure it has three steps: getting things ready, doing the dishes, and cleaning up. We are creating an abstract class AbstractRecipe. Any user who ants to use a recipe, does not need to worry about the fact that an AbstractRecipe exists. They just create an instance of the particular recipe, and call execute() on it.
Underneath all that, is the recipe implementation:
Our Journey With Encapsulation Thus Far
We looked at a few examples of how encapsulation is implemented. We saw that:
- At the very basic level, we achieve that by using built-in keywords such as private and protected around member variables
- The next level involves defining utility methods, to achieve encapsulation at a more logical level, to hide variable-level operations
- The next level of abstraction involves creating the appropriate interfaces, or abstract classes
- Another level involves the use of patterns such as Factory Pattern and Strategy Pattern
The thing is, encapsulation does not end at the code level. Let’s look at a few more instances of the same.
Encapsulation In Layered Architecture
When we build enterprise web applications, we normally like to structure them in layers:
The idea behind doing so is that, for example, you do NOT want:
- The Business layer to be too involved with storing and retrieving data
- The Web layer to know this, and also how business logic deals with data
- The Data layer to be bothered about how the application manages communication with other applications
We achieve this be creating an interface in each layer. For example, the Data layer exposes an interface which is used by the Business Layer. Similarly, other layers can also expose interfaces.
A Specific Example: JPA And Hibernate
Another good example of layered encapsulation is that of JPA and Hibernate. JPA is the interface, and Hibernate is the implementation. An application can make use of the JPA annotations, without worrying about the fact that underneath that interface, lies a Hibernate implementation.
Microservices: Common Components
Another example of encapsulation comes from the domain of microservices architecture. Have a look at the following architecture diagram:
Such architectures make heavy use of common components.
For example, look at security. Implementation of authentication and authorization is essentially the same across different microservices. By creating a common security component, you can abstract away how you store the security details of a user. It could be a database, or an LDAP server setup, or whatever.
The microservice will just end up using the interface that the Security component provides. The microservice will use the mechanism to ask whether a particular user is authorized to perform an action, but the implementation details are abstracted away.
This is another scenario where encapsulation is very much used. Suppose we implement one such service using REST. We expose a URL, as an access point for consumers of this service. We would also specify the formats of the messages to be exchanged with users. How the service is implemented underneath — what language and framework is made use of — does not matter to the consumer. All that matters is the interface.
Do check out our video on this:
In this article, we tried to answer the main question of what Encapsulation is all about. We saw that it is all about hiding the implementation and providing the right interface to the user. That user might be:
- A class using your class
- A component using your component
- A layer using your layer
- An end-user calling your service
We looked at encapsulation in great detail, at many different levels:
- At a basic level with member variables
- At a method level for better utility
- At an interface or abstract class level
- At a pattern level
- At an application layer level
- At a service component level
In particular, we saw how enterprise applications hide away things behind a component and provide an interface to it.
This is the fourth article in a series of articles on Software Design:
- 1 — How do you keep your design simple?
- 2 — Design Patterns For Beginners — with Java Examples
- 3 — What is Abstraction?
- 4 — Encapsulation — with examples
- 5 — Coupling — with examples
- 6 — Cohesion — with examples
- 7 — Introduction to Evolutionary Design
Encapsulation in Software Design was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.