In early days, programs were collections of procedures acting on data. A procedure is defined as a collection of instructions executed in sequential order. Data were independent of the procedures and programmers have to keep track of functions and the way they modify data. Structured programming is a simpler way to tackle this situation.
In structured programming, a complex program is broken into sets of smaller, understandable tasks; however, the drawbacks of structured programming soon became clear. These were, first, that data and task independency were too hard to maintain. Second, programmers were reinventing new solutions to old problems. To address these problems, object oriented programming came into existence. This provides a technique for managing huge complexity, achieving reuse of software components and coupling data with the tasks that manipulate data.
Object oriented programming, as a concept, was introduced by Xerox Corporation in the early 1970s. The first object oriented language was Smalltalk. The concept of object orientation in this language did not prove successful for about 10 years because of the limitations of early computers. Some languages that successfully implemented object oriented programmings are C++, Java and Eiffel. Object oriented programming is thought to be a relatively new concept in computer science. Contrary to this, all the major concepts such as objects, classes, inheritance hierarchies were developed by researchers of NorwegianComputingCenter as part of development of programming language Simula in the 1960s. Yet the importance of these concepts was slowly recognized by the developers of Simula.
Object oriented programming deals with things called ‘objects’. Objects are just extremely functional ways of organizing information. Objects in an OOP language are combinations of code and data that are treated as a single unit. Each object has a unique name and all references to that object are made using that name.
Allowing the programmer to write applications that interact with what appear to be physical objects makes the task of writing computer programs easier.
When an application is being created using OOP, the programmer needs only to remember a small set of flow-control commands and how to interact with the objects that his / her application uses.
Object oriented programming is built on the concept of reusing code through the development and maintenance of object libraries. These objects are available for building and maintaining other applications. Usually, a library contains the code for various functions that are commonly required during the program development. These libraries can be language-specified or user defined. Unlike traditional libraries, OOP languages provide object libraries which contain code along with the data in the form of objects, in addition to the names and entry points of the code located within. Objects of an object library are available for building and maintaining other applications.
We’ll be covering the following topics in this tutorial:
Characteristics of OOP
At the heart of OOP are three main characteristics. These are the features from which the advantages of using OOP are born.
• Encapsulation
• Inheritance
• Polymorphism
These characteristics differentiate object oriented programming from the traditional procedural programming model. They are now examined in more detail.
Encapsulation
An object encapsulates the methods and data that are contained inside it. The rest of the system interacts with an object only through a well-defined set of services that it provides.
The concept of sealing data and methods that operate on the data into an object (a single unit) is known as encapsulation.
Encapsulation of data and methods within an object implies that it should be self-governing, which means that the attributes contained in an object should only be modified within the object. Only the methods within an object should have access to the attributes in that object. If the state of an object needs be to changed, then there should be some service methods available that accomplish the change. That is, one should never be able to access and change the values of an object’s attributes directly from outside.
For example, embedding the attributes and methods defined for car in a single class car is referred to as encapsulation (see Figure (b)). The methods described in the car class can only modify/access the attributes defined in its class. Suppose, Zen is an object of car class, to set the parking light of Zen, the attribute parking_light can be set using the method set_parking_light_on; however, other objects of the same class such as Maruti; 800 or objects of any other class cannot access/modify the value of parking_light for Zen object.
Encapsulation helps in building better, structured and more readable programs.
Abstraction via encapsulation
Encapsulation facilitates abstraction. A software object is an abstract entity, in that we view it from outside without concerning ourselves with the details of how it works on the inside. For example, we do not need to understand the internal workings of the gears of a car to use it, we only need to know how to make it work. Because of this the source code for an object can be written and maintained independently of the source code for other objects. Also, an object can be easily passed around in the system.
Since, an object is a self-contained entity in a program; it can be referenced at different parts of the program wherever it is required. This feature enhances the modularity of the program. That is, the program consists of several modules, and if there are problems with any module, they can be addressed independently. Also, these modules can interact with each other without having to know the internal implementation of the other. Dividing a program into modules is called modularity; not having to know the internal implementation of a module is called abstraction. Hence, the feature of ‘abstraction’ facilitates a programmer to do ‘modular’ programming.
Data hiding
Encapsulation provides the hiding of data/information. It prevents users from seeing the internal workings of an object. The main reason for doing this is to protect data that should not be manipulated by the user. This makes the code more reliable and reusable.
An object has a public interface that other objects can use to communicate with it. The object can maintain private information and methods that can be changed at any time without affecting other objects that depend on it.
Every class can hide some of its parts. For example, if a part of code is called only by methods of that class, it can be made a private method, that is, invisible from outside that class.
Every element (field or method) of one class can have one of the three levels of visibility: public, private and protected.
• Public elements are completely visible from outside the class. They form the interface with the outside world.
• Private elements are visible only to methods of the class itself.
• Protected elements are something between public and private. To the outside world, they act as private elements, but they are completely visible to inherited child classes they have public behaviour for these classes.
Some benefits of encapsulation are the following:
• Improves program reliability
• Reduces maintenance
• Lends reusability
• Facilitates information hiding
• Improves modularity
• Makes coding easier
Inheritance
Inheritance is the feature of OOP by which the functionality of a class, called the parent or base class, can be extended by creating another class, called the child or derived class, which inherits some or all of the features of the parent class. The derived class has its own methods apart from the methods of base class and thus extending the functionality. This is the key for code reusability.
Inheritance is the process of creating a new class from previously defined classes.
When a derived class inherits from the base class, it will contain all the attributes and methods of the base class but adds new characteristics of its own. Any changes that are made to base classes are propagated to all derived classes that inherit from them, unless explicitly overridden. Changes to a base could fix or break an application.
Consider the example of the Car class. Suppose we want to create a new class called Maruti. We know that a Maruti is a car so we can derive this new class from our base car class. Some of the data for this new class may be size and mileage and a function would be engine. But since this is a derived class, we can also have access to the base class functions, accelerations and deceleration, plus the data of the base class type, colour and wheels.
Thus, inheritance can reduce the amount of coding by letting any derived class use the base class’s functionality when it needs to, thus simplifying implementation of similar classes and structures. In addition, the derived class is compatible with the base class, and can be used in place of the base class. This allows a system to override any of the base class functionality it wants and use the new objects where the base class would be used, thus adapting it to its own needs.
Derived classes may be formed in many ways. They can be derived from a single base class, or they can be formed from different parent classes.
When a class is derived from only one base class the process is called single inheritance. When a class is derived from more than one base class the process is called multiple inheritances. Multiple inheritances are used to combine two or more existing classes.
Inheritance is often shown diagrammatically in terms of an inheritance hierarchy or inheritance tree as shown in Figure. Note that the arrowhead points towards the base class to indicate that the child class is derived from it. In single inheritance the inheritance graph is a tree, whereas in multiple inheritances the inheritance graph is a directed acyclic graph (DAG). Figure is an example of a multiple-inheritance graph.
The advantages of using inheritance are the following:
• Increased productivity
• Reduced maintenance
• Standardization
The major drawback of inheritance is added overhead. This is explained below:
• An user must know changes in the methods of base class before using the derived class objects.
• Though the derived class objects may not require all the functionalities of a base class, the derived class implements all the methods of base class. This increases the amount of memory required for execution of the program.
• Since common functionalities exist in the base class, there is an execution overhead for derived class due to the implementation of base class methods when compared to having the functionalities in a single class itself. In the case of a chain of inheritances, the execution overhead may increase further.
Polymorphism
Polymorphism allows an entity (for example, an attribute, function or object) to take a variety of representations. It refers to the ability of an object to respond differently to the same message.
The advantage of this property is that the behaviour of an object varies depending on the message passed to it without worrying about the way the kind of message was passed. That is, an operation on an object may exhibit different behaviors in different situations. The behaviour depends on the data types and number of parameters used in the operation.
For OOP languages, polymorphism ensures reusability of higher level abstractions. This feature is also helpful in the implementations of inheritance where some base class behaviour needed to be overridden. Moreover, polymorphism facilitates describing variables that may refer, at run-time, to objects of different classes.
Overloading
Overloading is one kind of polymorphism. In OOP, there are two types of overloading: operator overloading and function overloading.
Operator overloading Operator overloading is the ability to use an operator on different argument types. For example, the operator ‘+’ is used to add either integers or floating point numbers. Another example is ‘-‘, which is used either negate an expression or to return the difference between two expressions. In addition, an operator can be overloaded on objects. Take an object complex number. We can add two complex numbers by overloading the operators ‘+’ and ‘=’, using the statement c3 = c1 + c2, where c1, c2 and c3 are values of the complex number type.
Another example of operator overloading is that an addition sign (+) can be used to specify addition of two numbers (say, 200 + 6 ~ 206) or concatenation of two strings (say ‘200’ + ‘6’ = ‘2006’).
Java does not support user-defined operator overloading.
Function overloadingFunction overloading is the ability to access different implementations of functions using the same name.
Consider an example that displays the current date. Usually, the date is represented in two forms:
• As a string: December 20, 2005
• As a triplet: (day, month, year): 20, 12, 2005
It is easier for a programmer if same function name can perform one of the above tasks depending on the user requirements. This can be achieved using function overloading by writing two different functions (methods) bearing same name as given below:
Printdate (StingStr) {…………… }
Printdate(int dd, int mm, int yyyy) {……… }
Here, the first Printdate function takes one string parameter as argument and the second function takes three integer parameters as arguments. Based on the data types of the arguments passed in a message, the corresponding function is invoked. Thus, two or more functions may have the same name, so long as the parameter types or number of parameters are sufficiently different enough to distinguish which function is intended. This feature is not allowed in procedural languages, as two functions cannot have same name. A function may not be overloaded on the basis of its return type. In Java, function overloading is referred to as ‘method overloading’.
There are two types of function overloading: early binding and late binding.
• Early Binding This allows us to have two functions in the same class that have the same name (function name) but with different number of parameters or different data types of parameters passed to it. The Print date function described above is an example of early binding. Early binding takes place during compile time.
• Late Binding This allows us to have functions with same name in base and derived classes. But the derived class function will override the base class function. Consider the example of geometric shape and its inheritance tree, as shown in Figure. We want to draw and fill the different shapes given in the figure. These two operations differ with respect to shape of the object and hence different functions have to be written as shown in Figure. Here, the base class functions draw( ) and fill() are overridden by the derived class functions. So, the function to be executed is selected dynamically during run-time, based on the shape object referred. This process of choosing the function dynamically at run-time is referred to as late binding.
The main benefit of polymorphism is it improves flexibility.
Overloading is not unique to OOP, it exists in procedure oriented programming languages also, such as C, to a limited extent. In C, we can use the operator ‘+’ for two integers as well as for floating point numbers. Similarly, the functions printf and scant can have different number of arguments with different types.