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:
- What is a GPU
- How does a GPU work
- 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
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())
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
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
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.
Top comments (0)