DEV Community

Cover image for The Encapsulation Principle Explained
Sakis bal
Sakis bal

Posted on

The Encapsulation Principle Explained

Image by pexels (Just drive it!)

Instructions Unclear

It’s Christmas, I got myself a new GPU as a gift and instructions as to how to add them to my computer. Not much time passed until I realized that the instructions book I got were not as clear as I hoped it would be.

Instruction book: Pages 410 😳

Contents:

  1. What is a GPU
  2. How does a GPU work
  3. Advanced Image Processing Algorithms

There’s not much to say. The book was bad.

Hell, it would be easier to just attend a whole semester of hardware classes instead of reading any more of this. It went on and on about things I never really wanted to know! All I wanted to know is how to put the GPU inside the computer and just play and chill a bit.

Now if that instruction book was a class inside a program it would have violated one of the most basic programming rules: Encapsulation, or else the art of exposing to the client only the important parts of your code, or the parts the client needs.

Why Encapsulation?

We have already explained what encapsulation is but to understand it better we need to see the alternatives of NOT encapsulated our class.

class Car:
  def __init__(self, speed, model):
    self.speed = speed
    self.model = model

toyota = Car(30, 'Toyota')

myCrazyWeirdCar = Car(50, 'Lamborghini')
myCrazyWeirdCar.speed = 41000
Enter fullscreen mode Exit fullscreen mode

Look, I would never call myself a mechanical engineer but even I understand there is something fishy going on here with this car. myCrazyWeirdCar is about to go outside the earth’s orbit and conquer mars if it continues with this speed, and that probably isn’t what the author of this code above wanted to happen.

Let’s fix this:

First I am assuming the car owner wanted to just get the car’s speed higher.

class Car:
  def __init__(self, speed, model, maxSpeed = 300):
    self.__speed = speed
    self.__model = model

    if maxSpeed > 400:
        self.__maxSpeed = 400
    else:
        self.__maxSpeed = maxSpeed

  def speedUp(self, speed):
    If speed < 0: 
        return

    totalSpeed = self.__speed + speed
    if totalSpeed > self.__maxSpeed:
        self.__speed = self.__maxSpeed
    else:
        self.__speed = totalSpeed

  def describe(self):
      return self.__model + " is going at " + str(self.__speed) + " km/h !"


toyota = Car(30, 'Toyota', 220)
myCrazyWeirdCar = Car(50, 'Lamborghini', 300)
toyota.speedUp(300)

print(toyota.describe())
Enter fullscreen mode Exit fullscreen mode

Now what we changed is that we gave our class attributes a little more privacy. (that is what the __ is before the attribute names). This means that these attributes are not accessible anymore directly!

No more Mr. 4000 car speed allowed. Or any sort of

myCrazyWeirdCar.speed = 40000
Enter fullscreen mode Exit fullscreen mode

Now, why is that a good thing?

The Problem With Not Encapsulating

To operate the car the client does not need to directly change the speed of the car. By giving the client the ability to change it we get three problems

The client knows too much about our class

He doesn’t know anymore what the class is supposed to do or what he is supposed to change. Do we use the class to have a data type that has a speed attribute that we can use? Do we use it to the model of the class and retrieve the model info? What is the class’s reason to exist?

The state of the Car class becomes unpredictable.

If the user is allowed to take any attribute anytime and change it directly to whatever he wants the Car Class becomes unpredictable and thus needs more and more checking in every method that we use.

For example, if the user could directly manipulate the speed to be a negative number then the car would be able to run at a negative speed. This would then need to be checked on every single method we are using.

Unable to change attribute names

Using the attributes directly leads to a problem where you can’t change the attribute names anymore because they are used in random places on your code. Changing one attribute name now requires you to check every file for its usage and change its name. This is most definitely not SOLID.

You against yourself

Now you are probably wondering: OK, but I AM the developer and I control these values so I can just like not change them to some unpredictable value?

Well, that is what many people including myself think. But then I get lazy at some point and just want the car value to change to some unpredictable value just for this one case, and then you end up making exceptions to support this one case and then your car isn’t a car anymore, and the story goes on.

You are primarily protecting you from yourself when creating a class. Remember that. So it’s better to isolate the attributes from a class and let them be private and just expose the functionality you need.

Take for example the class method from above:

def speedUp(self, speed):
    If speed < 0: 
        return

    totalSpeed = self.__speed + speed
    if totalSpeed > self.__maxSpeed:
        self.__speed = self.__maxSpeed
    else:
        self.__speed = totalSpeed
Enter fullscreen mode Exit fullscreen mode

Our car can accelerate to any speed we want without ever going more than the intended max speed. The user already inputs a max speed when creating the object so he expects the class to stop at max speed. He doesn’t need to know how it is done, he just inserts a speed and the method is encapsulating the logic behind it. Neat right?

In conclusion

If you want to help your future self-use encapsulation because:

  • It helps you write cleaner software and design your classes to exist without having them questioning their reason for existence.
  • Make classes reusable by hiding their complexity
  • Fewer checks per class method because of the unpredictability of attributes.

Oldest comments (0)