Firstly it is OOP and not OOPS, probably OOPS as an acronym became popular and has been widely used for many years so everyone just tags along. Anyways, OOP has 4 pillars
1. Abstraction 2. Polymorphism 3. Inheritance 4. Encapsulation
1. Abstraction :
It is the ability to generalize types i.e. it gives us the freedom to think about higher level concepts without going into too much details. Practically it is implemented with Abstract Classes and Interfaces. By using Abstract Classes or Interfaces we can create a software solution by focusing on broader concepts like AbstractCloudStorage or ICloudStorage and focus on what and how of AbstractCloudStorage or ICloudStorage or how they will interact with browser, OS, data center etc. instead of diluting our attention towards their concrete implementations like GoogleDrive or SkyDrive or iCloud etc.
Polymorphism (from the Greek meaning “having multiple forms”) is the characteristic of being able to assign a different meaning or usage to a variable, a parameter, a field, a property, a function, or an object in different contexts. There are fundamentally three types of Polymorphism, the first two of which were originally informally described by Christopher Strachey in 1967.
a. Ad-hoc Polymorphism which is popularly known as Method (function) and Operator Overloading.
b. Parametric Polymorphism allows us to have variable or field or parameter or property or function parameters of a function or a data type to be written generically instead of dependent on a particular type. This is popularly called as Generics in C# and one of the most popular example of it is List<T>.
c. Subtype Polymorphism (or inclusion polymorphism) is also popularly known as Overriding (though this is debatable). In C# a property or a method can be declared as virtual in base class which then we can override in derived class.
Another aspect of Subtype Polymorphism allows a function to be written to have parameter of a certain type T, but also work correctly if passed a parameter of a type S that is a subtype of T (according to the Liskov Substitution Principle). Subtype Polymorphism is complimentary to Inheritance.
Inheritance is a way to create new classes that absorb data and behavior of an already existing class and then enhancing them.
In other words, Inheritance is a way to establish “Is-a” relationship between objects
In classical inheritance where objects are defined by classes, classes can inherit attributes and behavior from pre-existing classes called base classes, superclasses, or parent classes. The resulting classes are known as derived classes, subclasses, or child classes. The relationships of classes through inheritance gives rise to a hierarchy.
However Inheritance more often than not results in the below Issues :
a) It is often confused as a way to reuse the existing code which is not a good practice because inheritance for implementation reuse leads to Tight Coupling i.e between the base class and derived class. This means when changes are done in the Parent class, mostly changes need to be done in the derived class or how the derived class is used; resulting in low maintainability. To understand this kindly visit this link. Re-usability of code should be achieved through composition (Composition over inheritance). Another link I would highly recommend which tells why we naturally (i.e. our thought process) lean more towards Inheritance and why usually that is not such a good idea.
b) yo-yo problem is an anti-pattern that occurs when a programmer has to read and understand a program whose inheritance graph is so long and complicated that the programmer has to keep flipping between many different class definitions in order to follow the control flow of the program.
d) Inheritance breaks encapsulation by exposing subclasses to implementation details in the superclass. That is the sub class will always have all the data and behavior of parent class and we can not choose a specific data and behavior of base class to available for derived class.
It is the ability to wrap “something complex” or “something which had many steps” into a single; easy to use container. Practically it is implemented with Functions or Methods. For example the below method does many things like validating customer information, opening data base connection, firing a query or SP, or use ORM framework to persist customer information in a DB. But the caller of this method need not to worry all this and just need to eat this capsule or call this method.
One more thing Information Hiding / Data Hiding / Hiding (in General):
It is the ability to hide unnecessary data and behavior from the end user. Here if you are writing a library your end user is some other software, if it is a class, end user is other classes of the same program etc. Practically it is implemented with Access Modifiers i.e. public, internal, protected, private in .Net world and with Properties and Fields.
By using Access Modifiers we are limiting the visibility and usage of methods (especially), fields, properties to the end user and hiding unnecessary stuff.
On the other hand, by using Properties to Get and Set data we are hiding how actually data is actually processed or manipulated or persisted by the class.
For example in the below example if the end user i.e.Program class’s Start method forget to assign Currency field of Amount object, then while setting the Amount property we can check this and set it as per current region of the computer. What I am trying to demonstrate is; by using Property we have hidden the behavior of checking and assigning currency and this will make sure whenever we Get the Amount it always has Currency.
A similar example of Data Hiding can be when Setting Amount we can normalize it to USD and store it and then while Getting it we can do the appropriate currency conversion as per the computer’s culture.
All these concepts are complimentary (rather than competing), so when you are discussing about one, you will never know when you have ventured into the territory of other concept. In my opinion a pragmatic programmer would be able to create elegant code by having strong hands on with OOP concepts. Thus OOP truly results in a code base which is Extendible, Maintainable(Changeable), Scalable, with Low Coupling and High Cohesion.
Side Notes :
A pure virtual function or pure virtual method is a virtual function that is required to be implemented by a derived class, if that class (the derived class) is not abstract. Classes containing pure virtual methods are termed “abstract”; they cannot be instantiated directly. In C# pure virtual functions are known as abstract methods.
Overloading is Compile time polymorphism, Overriding is runtime polymorphism.
We can create a long class hierarchy (using) inheritance, however it doesn’t mean we should.