Encapsulation is one of the core concepts of Object Oriented Programming (OOP). Mainly, encapsulation binds together the code and data in a single unit of work (a class) and acts as a defensive shield that doesn’t allow the external code to access this data directly. It is the technique of hiding the object state from the outer world and exposing a set of public
methods for accessing this state. When each object keeps its state private
inside a class, encapsulation is achieved. This is why encapsulation is also known as the data-hiding mechanism.
The code that takes advantage of encapsulation is:
- loosely coupled (eg., I can change the names of the class variables without breaking the client code)
- reusable
- secure (the client is not aware of how data is manipulated inside the class)
- easy to test (it’s easier to test methods than fields)
In Java, encapsulation can be achieved via the access modifiers/specifiers, public
, private
, and protected
.
Everything is public
by default.
- To make fields/variables and methods
protected
, prefix them with a single underscore '_'. Eg.,_field
ordef _method()
. - To make fields/variables and methods
private
, prefix them with a dunder '__'. Eg.,__field
ordef __method()
.
Commonly, when on object manages its own state, its state is declared via private
variables and is accessed and/or modified via public
methods. For example, a Cat
class can have its own state represented by fields, such as mood
, hungry
, and energy
. While the code external to the Cat
class cannot modify any of these fields directly, it can call public
methods, such as play()
, feed()
and sleep()
that modify the Cat
state internally. The Cat
class may also have private
methods that are not accessible outside the class, such as meow()
.
public class Cat {
private int mood = 50;
private int hungry = 50;
private int energy = 50;
public void sleep() {
System.out.println(“Sleep …”);
energy ++;
hungry ++;
}
public void play() {
System.out.println(“Play …”);
mood ++;
energy --;
meow();
}
public void feed() {
System.out.println(“Feed …”);
hungry --;
mood ++;
meow();
}
private void meow() {
System.out.println(“Meow …”);
}
public int getMood() {
return mood;
}
public int getHungry() {
return hungry;
}
public int getEnergy() {
return energy;
}
}
The only way to modify the state is via the public methods, play(), feed(), and sleep().
public static void main(String[] args) {
Cat cat = new Cat();
cat.feed();
cat.play();
cat.feed();
cat.sleep();
System.out.println(“Energy: ” + cat.getEnergy());
System.out.println(“Mood: ” + cat.getMood());
System.out.println(“Hungry: ” + cat.getHungry());
}
The output is as follows:
Feed …Meow!Play …Meow!Feed …Meow!Sleep …
Energy: 50
Mood: 53
Hungry: 49
Here's the Python equivalent of the Java code, using Python property decorators to demonstrate encapsulation in Object Oriented Programming:
class Cat:
def __init__(self):
self._mood = 50
self._hungry = 50
self._energy = 50
def sleep(self):
print("Sleep…")
self._energy += 1
self._hungry += 1
def play(self):
print("Play…")
self._mood += 1
self._energy -= 1
self._meow()
def feed(self):
print("Feed…")
self._hungry -= 1
self._mood += 1
self._meow()
def _meow(self):
print("Meow…")
@property
def mood(self):
return self._mood
@property
def hungry(self):
return self._hungry
@property
def energy(self):
return self._energy
if __name__ == "__main__":
cat = Cat()
cat.feed()
cat.play()
cat.feed()
cat.sleep()
print("Energy:", cat.energy)
print("Mood:", cat.mood)
print("Hungry:", cat.hungry)
The output is as follows:
Feed…
Meow…
Play…
Meow…
Feed…
Meow…
Sleep…
Energy: 51
Mood: 52
Hungry: 52
In this implementation, the mood
, hungry
, and energy
fields are declared as protected using the leading underscore notation. The get_mood()
, get_hungry()
, and get_energy()
methods are replaced with Python property getters (declared using the @property decorator).
This way, the internal state of the Cat
object can be accessed using the properties (eg., cat.energy
), but the underlying fields cannot be directly modified.