Encapsulation, Inheritance, Abstraction, and Polymorphism … oh my

Encapsulation

I like to think of encapsulation as group like attributes together in a way that makes sense. A Class is, in a sense, a template to follow when instantiating (creating) an Object. A Class defines the characteristics (attributes) and behaviors (methods) of the group of characteristics. For example, if we are designing an application that tracks personal contacts, then a Person class would look like:

spacer

Do we have to make name private? No. Data hiding, in the strictest sense, is an architectural (or design) decision. We may elect to hide data that, if changed in an unexpected manner, will cause data corruption within clients using the Object. For example (and this may be a poor one, but bear with me), if we decide that the name of a Person will always be in lowercase letters, then we may write the following:

Now the name attribute of a Person will always be either an empty String or in lower case letters. We have hidden name within the Person class, so the only way to access the name attribute within the Person class is through the use of the provided methods.
There is a distinct difference between encapsulation and data hiding. One is where like ideals and concepts are grouped together in a meaningful fashion, and one relates to how the data can actually be accessed.

Inheritance and Abstraction

Inheritance is when one class extends the characteristics (attributes) and behaviors (methods) of another class. Just like inheritance when a loved one dies, you get what was left to you in the will. If the loved one left everything to you, then you get everything. If the loved one left you nothing, then you simply shared a life with that person, especially their last name (if married). So it is with OOP. Inheritance is based upon what is visible to the subclasses (child, of Class that extends a Class) from the superclass (the parent Class, of the Class that is extended by the child Class).

If we wanted to develop an application to process documents, we may do the following:

When we run the ExampleDocumentProcessor, we see that each Class we just created can be instantiated into a Document Object; Each Class inherits the name attribute provided in the Document Class. Each of the subclasses (children) inherits attributes and methods from the superclass (the parent).

Suppose we do not want just any old Document created. We want a specific type of Document created, so we can process each Document in the same manner. We can use abstraction to prevent the instantiation of a Document.

Our revised code looks like this:

Each of the subclasses still have a name attribute, but through abstraction and data hiding, we are able to prevent the Document Class from being instantiated. Actually, for our example, we do not want just a plain old Document to process, because we may not be able to offer a default implementation of how to process a plain old Document. In the example offered, we expect to process a PaperDocument and an EmailDocument differently.

Polymorphism

We can introduce polymorphism into our design, allowing for each type of Document to handle processing a different way, based upon what is expected. If we introduce an interface (or two), a way to provide expected behaviors to implementing Classes, we can then process each type of Document in an expected way. The revised code now looks like this:

So … what we have is the following:

  • An interface that allows for implement classes to have the same type of expected behavior (process()).
  • We have an abstraction of the expected characteristics and behaviors of what each type of class that extends the superclass Document is supposed to have available (each Document type will have a name, and a processing implementation).
  • Each different type of Document inherits characteristics and behaviors from the Document Class.
  • We have provided a way to introduce new behaviors to each type of Document Class through the use of interfaces

This is not the best design possible, but it serves to illustrate the various basic OOP concepts..For example, the use of a cast to process an EmailDocument is probably not the best approach. A software architecture evolves over time, and is rarely something that is created correctly the first time around. Design considerations are sometimes not always known when design decisions are initially made. Mistakes will be made, even after fully understanding OOP concepts. The important thing to remember is many design issues have already been addressed in the past through the use of design patterns.

I hope you have enjoyed this brief introduction to OOP concepts. For further reading, please see the following:

Lesson: Object-Oriented Programming Concepts
Encapsulation is not information hiding
Java – Encapsulation
Java – Inheritance
Java – Abstraction
Java – Polymorphism
How encapsulation is different from abstraction as a object oriented concept in java?
4 major principles of Object-Oriented Programming
Why getter and setter methods are evil
What is polymorphism in java