For what I know, encapsulation is useful because:
- if you use directly an attribute and change its type in a static typed language you have to change all the code that uses the class. On the contrary, if you have declared getters and setters for that attribute you have not to do this
- it hides the internal functioning of your class, useful if, for example, you’re offering an API
- it avoids that another class that inherits your class will overwrite attributes or methods
My considerations about dynamic typed, interpreted programming language are:
- since the types are dynamic there’s no need to encapsulate your class for this reason
- you can see the code of an interpreted language, so I think there’s not a way to really hide your API
- this could be a problem when this is unintended, but my opinion is that it should be better to make attention to unintended overwriting instead of limiting by default this possibility. Python is a good example: if you declare for example, the name of an attribute or method starting it with a double underscore, you can declare it also in a inherited class, but they are threated as two distinct variables even if they have the same name (thank you delnan)
a method name that starts with a “_” character raises a warning if overwritten by a method of an inheriting class.
Am I missing something important?
15
Yes, you are missing something.
Encapsulation is often described with metaphors of “hiding” or acting “defensive”, as if client programmers were your enemy and you had to act stealthily and sneakily to defend yourself against their evil intentions. That is sometimes helpful; for instance, if you program extremely popular frameworks that have to maintain compatibility across a huge number of situations and versions, you start to think of your users as ignorant barbarians whose only intent in life is to cause you trouble with their unreasonable demands of “But it used to work!” or “But I can see that it’s an array internally! Why, why, why won’t you let me access the elements directly??”
However, the normal case is that the layer using your code is programmed by the same person as your own layer: yourself. Therefore point is not actually to hide information from other routines and their creators, but to free its caller from having to understand it. The trick is to make it possible to write higher-up routines without knowing about the details. The cognitive delusion that if you created something yourself, you are obviously going to understand everything it does is extremely common and extremely strong; large parts of software engineering are dedicated to mitigating the consequences of this error of thought. If you program a subroutine, but you need to know how it works internally in order to use it properly, then you might as well not have a subroutine, because the mental effort necessary isn’t actually reduced: the code looks clean and modular, but actually isn’t.
This is why we separate big amounts of code into small partitions: because a small amount of code is easier to understand than a large amount, and crucially, two properly decoupled small bits of code are easier to understand than one bit twice the size, because interdependencies within the larger bit add cognitive effort. If we don’t avoid that extra effort, sooner or later it will exceed our capability to understand the entire system, and it soon becomes unmaintainable.
5
You are forgetting two things.
First, encapsulation is first and foremost about keeping the amount of coupling between your modules low in a structured way, i.o.w., keeping your code modular. Proper encapsulation separates interfaces from implementations, and being able to change internal workings without touching the interface goes much further than simple getters/setters, and covers more than just return types. Take, for example, a rectangle. There are two possible representations: (left, top, right, bottom)
, or (left, top, width, height)
. Neither is better, but either may be more suitable for certain situations. If you make getters/setters for all six properties (left, right, top, bottom, width, height), you can change the internal representation in 5 minutes, without touching any other code.
A more important reason, though, is that encapsulation limits the scope of fields and methods. This means that when you want to change a private field, you only have to take its containing class into account; if it’s public, you have to check the entire codebase for possible regressions. By extension, reasoning about properly encapsulated code is easier than code where everything is openly accessible from anywhere.
5