Han Solo was a good guy. He was however unfortunate at times.
Image credit: https://flic.kr/p/h8jDb
Yep, that's right, as you likely know Han Solo was encased, even encapsulated in carbonite by the notorious gangster Jabba the Hut. You see Jabba was taught in space gangster school that in order to keep his system working as designed he needed to encapsulate his system behind a rock solid interface. Through his vast experience collecting debt he had come to know that carbonite fit the bill. However, unfortunately for Jabba, he didn't have a true understanding of encapsulation. He just took the best practices he had gained in school and directly applied them, without grasping the principles. This ultimately led to his downfall at the hands of a rag-tag team of do-gooders at a Sarlacc pit.
The lesson here? Let's understand the principles of encapsulation, not just the tactical best practices and idioms. For example, I remember being taught that we always make properties (or data members, or fields, etc) private and then only operate on them through setter and getter methods. Asking "Why?" makes for a fun discussion. If you have a class with a property, and you want to make access and modification of that property publicly available to anyone using your class, then why not just make the property public?
Think on that for a second. I find that many people never have.
One thought is that assigning a setter facilitates the ability to do error checking on the property. This is true, but incomplete. Even for properties that don't require any validation it is drilled into our heads that we should have the empty-boiler-plate-pass-through setters and getters.
Ok, then why else? I don't deny the need in many languages to have these methods to encapsulate private data. However, for me, the principle at play is that doing so follows the principle behind encapsulation, which is information hiding. In many languages, these boiler plate setters and getters allow the software to separate out its implementation from its interface. This enables the software to evolve from its current implementation to another implementation that fits the needs of future me, which of course I don't know yet (even if someone out there does). If we expose fields or properties in languages like C++ or Java® this flexibility is lost. Yes it is in our interface that we can access and modify a given property, but when we expose the property directly as public this also exposes implementation. Specifically, it exposes a couple things right off the bat:
- The property is actually stored on the object (this object even!) and is in memory.
- The property does not (and cannot ever) be subject to input validation when modified and can never go through preprocessing when accessed.
Even if today there is no input validation, or the property is held in memory on the object, adding these setters and getters is important to allow the flexibility to change our minds in the future. Maybe in the future we refactor and would like to delegate the ownership of the property to another object. Perhaps we would like to calculate the result every time it is accessed. If we have the property encapsulated properly this sort of change is not a problem at all.
So, in MATLAB does it follow that we should make all properties private and write our own getter and setter accessor methods? Absolutely not! In fact, unlike many other languages, properties in MATLAB are already encapsulated. For example, see the following class:
classdef Carbonite properties(SetAccess=private) HanSolo end end
As you can see, the HanSolo property restricts modification, but allows access to the property. That is the only thing in the interface. Note that the following different implementations all still are faithful to that basic interface without the client code ever being the wiser. What is in the interface is the what, what is encapsulated is the how.
Delegation - assign someone else to to the work:
classdef Carbonite % This implementation delegates to some other class. properties(Dependent, SetAccess=private) HanSolo end properties(Access=private) HanSoloAccessor = HanSoloDelegate; end methods function solo = get.HanSolo(carbonite) solo = carbonite.HanSoloAccessor.HanSolo; end end end
Calculation - Don't store anything, recalculate every time:
classdef Carbonite % This implementation creates a new HanSolo every time properties(Dependent, SetAccess=private) HanSolo end methods function solo = get.HanSolo(carbonite) solo = utils.createNewHanSolo; end end end
Preprocessing - Add a bit of flavor in a getter, do some error checking in a setter:
classdef Carbonite % This implementation preprocesses the stored value of HanSolo before % giving it back, this time with his trademark hat from a different set % of movies. If the property was modifiable, you could add a setter to % perform error checking when modifying the value. properties(SetAccess=private) HanSolo end properties(Access=private) IndyHat = IndianaJonesFedora; end methods function solo = get.HanSolo(carbonite) solo = carbonite.HanSolo; solo.add(carbonite.IndyHat); end end end
Ok you get the point. MATLAB's model for properties doesn't break encapsulation, so there is not need for the boiler plate setters and getters. You can expose a property's set and get access to match whether you want it to be accessed or modified at all, and you can do this without exposing the implementation details because you can always add the set.Property or get.Property methods to change the implementation. The implementation is still, say it with me.... encapsulated!
So what was Jabba's mistake? He didn't understand the notion of a leaky abstraction. He still exposed some of his implementation because he was so proud of his HanSolo prize that he showcased Han Solo as his favorite decoration. While Jabba thought he was encapsulated in the carbonite, he didn't leverage encapsulation to achieve information hiding. Everyone knew what was inside that capsule, including a newly minted Jedi and his friends (one of whom was an extremely loyal wookie I might add). If Jabba had hid his prize, or perhaps if his carbonite was a little thicker so nobody could tell the difference between the Han Solo slab and all the other smugglers who were late in their debt payments, perhaps he would still be alive a long time ago in a galaxy far far away.
...or what if he used this implementation?
classdef Carbonite % This implementation clones HanSolo each time as a decoy to pesky Jedi. properties(Dependent, SetAccess=private) HanSolo end properties(Access=private) RealHanSolo end methods function solo = get.HanSolo(carbonite) solo = clone(carbonite.RealHanSolo); end end end
That would have had the nice effect of punishing the real Han Solo while getting his friends off his back.
Takeway, we should understand the principles behind encapsulation, not just the formulas. Let's not be like Jabba, his software wasn't any good anyway.
Get the MATLAB code
Published with MATLAB® R2016a
6 CommentsOldest to Newest
Another great post Andy. Thanks again for putting the time into writing these. More OOP, design pattern & unit testing goodness please.
Thanks David, we’ll keep ’em comin!
Thanks for the post! How would this work with inheritance?
In your example you’ve created five different implementations of the Carbonite class that all have the same interface, namely HanSolo. If I wanted to move HanSolo to an (Abstract) Interface class so that these and future classes could inherit it, how would I best do so? If I make the interface’s properties abstract, it seems to work; but I run into issues when I try to create a new class that extents a concrete class and implements the interface because the concrete superclass defines/implements properties in the interface.
As far as I can tell, derived classes can’t override superclass concrete properties (or change abstract superclass Access attributes) like you can methods. If a concrete superclass defines a property as Dependent (or not) then all derived classes are stuck with that implementation, which seems to me like interface and implementation are not completely separated in properties. Am I missing something? Thanks.
Great question. From where I stand, I see this behavior of properties as fully consistent with encapsulation, and the question brings the topic of polymorphism into the discussion.
Polymorphism is the principle that concrete classes are able to define their implementation of specific behaviors/methods/messages. If one defines a base class with an abstract method, all the subclasses can define the behavior of that method, thus subscribing to that interface but have their own implementation. Even if a class defines a method as concrete, assuming it is not Sealed, polymorphism still allows that subclasses can override that method and define it specifically for the subclass.
Overriding concrete methods is a powerful tool for class design, but can also be very dangerous, and I would say it is a good practice to only do this when the base class has been specifically designed for these methods to be overridden (for example,in the template method pattern). The problem is this behavior actually can break encapsulation of the base class when one overrides a method that the base class was not intending to be overridden. Remember, base classes are encapsulated from their subclasses as well!
Now, finally I am getting to properties. It is true that you can’t “override” properties and I can think of at least a few good reasons. For one, the properties may already have storage allocated on the base class. Likewise it may not allocate, remember it is encapsulated, but the point is we don’t know at the subclass level. Also, in these scenarios, the base class also should not know anything about the subclasses, so if the subclass wanted to redefine the property from the base how would the base class know whether or not, for example, to itself allocate storage for the property at the base class level. Doing so would _break_ encapsulation. Another good reason is that there are really two “encapsulated sugar” methods per property – the set.Prop and the get.Prop method. If we tried to override one and not the other we could definitely run into problems where the base class has a setter/getter defined assuming its implementation and the derived class “overrides” the setter. Now all of the sudden the getter and setter are out of agreement and defined in entirely different contexts which know very little about the implementations of the other (again, due to encapsulation).
Finally, perhaps a good way to think about it is that the interface and implementation are still very separate, its just that the implementation has already been provided. Certainly the subclasses can operate on that property’s interface all day long but the defining class is the only one that should be allowed to process the sets and gets of that property. For example, perhaps you may be looking to just make the base class property accessible to be set/modified by the derived class through something like protected or pubic access permissions. Then, the notion of “overriding” really means changing the value of the property in the derived class.
Again, great question and very interesting to think about. Perhaps I can blog about it a bit.
[…] 8 Aug Encapsulated Sugar […]
[…] we discussed a good reason to appreciate and use MATLAB's model for properties. It had to do with encapsulation if you remember, and we discussed how MATLAB's properties are inherently encapsulated, preventing the need to write […]