The Paradox of Polymorphism and Encapsulation in OOP

I went to a small, liberal arts Christian college outside of Chicago, known to some as the “Harvard of Christian colleges.” It was definitely a bubble in many ways. But are bubbles always bad? What if that bubble actually encourages more interaction with the world outside of itself?

For example, because I was in this protected environment, I was able to question aspects of my faith that I would not have been able to outside of that bubble. Let’s take evolution, a topic that, if you had asked me before I went to college, I would have said that all Protestants/Evangelicals agree: it’s not biblical. However, within that bubble, I was able to see and trust the diversity of beliefs surrounding evolution, to such an extent that I can unabashedly say that not only do I believe in evolution, but it’s also a moot point.

Because of the bubble, I was actually able to expand my understanding of my faith. I wasn’t so fearful of letting go of what I once thought were sacred tenants of evangelicalism. I was able to open up to other perspectives because I was able to trust in my bubble while I was exploring. I can now interact with people of different faiths or people without any religious affiliations about their beliefs without feeling defensive. Their beliefs have enriched my own beliefs. I am able to fully celebrate diversity because I can accept the particularity of my own faith.

So here’s how this relates to Object Oriented Programming: bubbles are not bad. Protecting data can actually give it the ability to have more complexity in its relationships with the program around it. Ruby accomplishes this seemingly paradoxical relationship between protection and interaction in the concepts of Encapsulation and Polymorphism.

OOP is a paradigm for dealing with the complexity of large programs. One problem with a large program is that one small change can affect the whole program. OOP deals with this by encapsulating code and reducing dependencies between different parts of code, so that changes in one part of the program won’t break the program somewhere else. The other problem with a large program is repetitive code: OOP uses polymorphism to reduce the amount of repetitive code in a program.

Encapsulation is part of OOP’s response to the complexity of programs that hides parts of the program from other parts of the program to protect data. This bubble allows the unit (in Ruby’s case, the object) to have its own unique attributes (in Ruby, instance variables). The programmer very purposefully decides whether or not she wants different attributes or behaviors to be accessible to other parts of the program or to the public interface. This allows for new levels of abstraction and the ability to represent real-life objects and behaviors. Through the use of objects and classes, changes (either intentional or not) to one part of the program do not affect the entire program. It is easier to debug, and it allows for better control. Ruby’s form of control is not a 1984 type of control, but rather more related to the control one feels when practicing self-care: I can control my own actions and my own narrative about myself. The control is not imposed from above, but rather from within the system itself, each object delineating its own scope. It might take a little more work to set up, but in the end, it actually requires less micromanaging than a procedurally-written program.

Whereas encapsulation reduces the interactivity of different parts of the program to protect itself, polymorphism is about expanding the abilities of the encapsulated parts of the program to interact with the different parts of itself. Polymorphism means “many forms” – it is a way for pre-written code to be used in different and sometimes new ways by various objects in a program in response to the same method invocation. It is also a way to DRY (don’t repeat yourself) up the code and reflect real-world, logical hierarchies and common behaviors.

Polymorphism in Ruby is found in inheritance and the use of modules, which are both complex relationships that allow for cleaner, less repetitive code. Common logic can be extracted from several different classes into a hierarchical superclass in inheritance, or common behaviors can be shared among unrelated classes through the use of modules, which allows us to change code once instead of having to change it in several different places.

An object-oriented program is generally healthier than a procedural program. There are always tradeoffs, but in general, OOP provides a way to manage complexity by making the code simultaneously more particular and more universal. Once you see this way of understanding code, it’s hard to unsee it. Kind of like my faith – I understand where the old me was coming from, but I can’t go back to how I thought and lived before. And I wouldn’t have it any other way.