My good friend Sam Stokes posted an introduction to Encapsulation not long ago. Not that I am competitive mind you but I thought I could do better. And since I had some vacation time in which to think and write I have given it a try. I’ll let you judge if I succeed or not.
So what is Encapsulation? Encapsulation is an important part of object oriented programming. It is part of what makes OOP “safer” and more easy to use. Briefly stated Encapsulation is the hiding of implementation details of a class. Specifically you include the data storage as private data and create methods that interact with that data. The implementation of these methods is not visible to the programmer who uses the class. Nor is the internal representation of the data. Everything is in this software capsule – this “black box” that just works. One fun example is a die class (Die being the singular of dice in this case. Nothing to do with first person shooter actions)
Of the several benefits of encapsulation are that data cannot be directly manipulated by code that calls/uses the object and that the way methods work can be changed without negative impact on other code that uses the class/object. Let’s take a look at this in code with the first part of a very simple Die class (This example is in Visual Basic but the concepts are the same in other programming languages)
Public Class Die Private sides As Integer Private face As Integer Private rnd As Random Public Sub New(ByVal s As Integer) If s < 2 Then Throw New Exception("Sides must be greater than or equal to 2") End If sides = s rnd = New Random face = rnd.Next(1, sides + 1) End Sub Public Sub New() Me.New(6) End Sub
The first thing of note is that our data is declared as Private. This means that these variables are only visible and modifiable from inside the class. Code that calls an object of Die class can not access sides, face or rnd directly. They are “hidden” – this is what we mean by data hiding. Are two constructors, one which allows the specification of the number of faces the die has and one that creates a default die with six sides, can manipulate the data but do so transparently to the user. The non-default constructor does some simple data checking. In this case it only allows the creation of a Die with two or more sides. This is for the user’s own protection. Notice that the default constructor calls the non-default constructor. This is not required but is a good idea because it means that the actual setting of face happens only once and with proper data validation happening in one central place. We also only need to set the initial value of the Die in one place. User access to the data happens through methods. As we see below.
Public Function Roll() As Integer face = rnd.Next(1, sides + 1) Return face End Function Public ReadOnly Property FaceValue() As Integer Get Return face End Get End Property Public Overrides Function ToString() As String Return face.ToString() End Function Class
In this example we only allow the user to get (and change in a protected fashion) the face value of the Die. The property FaceValue is read only – we do not want the user code changing that to some value of its own choosing. In fact the only way to change the value is to call the Roll function/.method which uses random values that are (theoretically at least) outside the user’s control. The ToString method is likewise only a way to get the face value.
In our example there is no way to retrieve the number of sides the Die has and fixing that would probably be a good edition. Like FaceValue that would be done as a read-only Property.
In our example we could substitute any algorithm for changing the face value of our Die that we wanted to as long as it returned an Integer value within the range expected by the user code. While we are probably not going to write our own random number routine we could and it would not require any changes in code that used this Class. In other applications being able to change the algorithm used in a method without having to change code that uses it could be a valuable option. It would allow us to test various algorithms for performance, accuracy, or other values quickly and easily. In a payroll program we could make changes in the calculations for bonuses, overtime or any number of things within a single class without having any negative impact on programs that used that class. That’s a big win in ease of testing and debugging. It’s all made possible by encapsulation.