The second fundamental principle of Object Oriented Programming is called encapsulation. Its main definition refers to the action of hiding anything that is not essential from the outside world. It is not very difficult to understand the fact that we do not need to expose everything when we build something. For instance, if we were engineers and we would design a car, we would not want the users of our car to be able to access stuff that is not relevant to them. In other words, the users do not need to know about the internal wiring, the mechanical parts, etc. They do not need to know and they should not know of such details. The internal wiring and mechanical parts should only be accessed by qualified personnel, so we would hide them from unauthorized access.
Therefor, we use encapsulation for two main reasons: we hide non-essential data from the outside world because the outside world is not interested in that data, and we hide it because we don’t want the outside world to modify it in ways that was not intended.
In C#, encapsulation is achieved using access modifiers, of which we already talked about. In retrospective, we understand that:
- private access modifier is the most restrictive access modifier, because it only allows us to use members from within that particular type (a private method can only be used from within the class that contains it). It is also the default access modifier of members, meaning, any variable, property or method is marked as private by default, if we do not specify an access modifier.
- protected is an access modifier similar to private, with a single difference. Protected members are only visible from within the class where they are declared, and from within any class that inherits from the class where they are declared.
- internal access modifier lets us use types and type members only from within the current assembly. This mainly means that we are able to use them from all our project. It is also the default access modifier for types. If we declare a class and we do not specify an access modifier for it, it is marked as internal, by default.
- protected internal is similar to a combination of both internal and protected access modifiers, which means that it allows the usage of code from either a derived type, or from within the executing assembly.
- public is the least restrictive access modifier, and it poses no restriction is usage of types or members.
As we can see, we have plenty of levels at which we can enforce encapsulation, effectively showing internal workings only to whom should know about it. Encapsulation is not only allowing us to hide irrelevant stuff to the user, but also allows us to enforce desired behavior. For instance, if we want to store the age of a person, we could declare a public int variable. But that way, users can assign negative values to it, which would be entirely wrong. No one is -5 years old. What we can do to correct this aberration is to make the int variable private, and only assign values to it through a property or method. This way, we store the value of our int variable hidden from the user, and inside the property or method where we allow assigning a value to it, we can specify conditions, such as age being a positive number.
Another benefit is the fact that it allows developers to change things without breaking functionality. For instance, if we have a class Animal that implements a walking behavior, it would probably have methods like FirstPawForward(), FirstPawBackward(), SecondPawForward(), SecondPawBackward(). In order for the user to make the animal walk, he/she should know the exact sequence of calling these methods, so the animal walk in a proper, intended way. But what if we later decide that our animal hops, and not walks? We would need to change these methods to HopForward() and etc. But, if we rename the methods, all the codes that relied on calling these methods would not know about the new names anymore, and we would get errors. The user would have to find and replace every method call with the new name and behavior. That is something horrible to do. Instead, we can mark all these methods as private, and expose a single public method named Move(). Inside this method, we can call any private methods we want, that actually perform all the required actions to walk, hop, crawl, whatever. Whenever we want to change this behavior, we only change what’s being called inside this Move() method, but the users wouldn’t know the difference. From their perspective, they still see a single public method called Move(), and nothing in their programs breaks.
As a general rule, get yourselves used to hiding as much as possible from your codes. Only expose the minimal requirements to the outside world.
Tags: access modifiers, encapsulation, object oriented programming, OOP