The object-oriented programming is created as a response to increasing complexity of the programs that caused problems for the process-oriented model of programing. Object-oriented programming organizes a program around its data (objects) and a set of well-defined interfaces to that data. An object-oriented program can be characterized as data controlling access to code. Object-oriented programming is based on four concepts: Abstraction, Encapsulation, Inheritance and Polymorphism.
Abstraction as a concept is present in our everyday life. We don’t think of a phone, a car, or a computer as sets of thousands of individual parts. We think about this objects as whole well-defined objects whit its own unique behavior. This kind of abstraction allows us to use the phone or the car without being overwhelmed by the complexity of the individual parts. We don’t need to know how the phone or the car works under the hood, we are using this objects as a whole.
The best way to manage abstraction is through the use of hierarchical classifications. This allows you to layer the semantics of complex systems, breaking them into more manageable pieces. When you see a car from the outside it’s a single object. Once you get inside, you see that the car consists of several subsystems: steering, brakes, sound system etc. Each of these subsystems is made from more specialized parts. We know how to start the car and how to steer the car. So we only need to know a certain set of actions on how to use the car. We don’t need to know how the internal-combustion engine works to drive a car.
The hierarchical abstractions of complex systems are applied to programs. We have component objects with a collection of messages between these objects, so each of these objects describes its own unique behavior. You can treat these objects as concrete entities that respond to messages telling them to do something. This is the essence of object-oriented programing.
When done right abstraction can make changing and maintenance of the code easier. Once you have well-defined objects and clean, reliable interfaces to those objects, you can easily decommission or replace parts of an older system without fear of harming the code.
Encapsulation is the mechanism that bids together code and the data it manipulates, and keeps both safe from outside interference and misuse. One way to think about encapsulation is as a protective wrapper that prevents the code and data from being arbitrarily accessed by other code outside the wrapper. Access to the code and data inside the wrapper is tightly controlled through a well-defined interface.
Real world example of encapsulation: automatic transmission on a car. It encapsulates hundreds of bits of information about your engine, such as how much you are accelerating, the pitch of the surface you are on and the position of the shift lever. You as a user have only one method of affecting this complex encapsulation: by moving the gear-shift lever. You can’t effect the transmission by using the turn signal or windshield wipers. So the gear-shift lever is a well-defined, unique interface to the transmission. Further what goes on inside the transmission does not affect objects outside the transmission. For example shifting gears doesn’t turn on the headlights. Because an automatic transmission is encapsulated, dozens of car manufacturers can implement one in a way they please. However, from the driver’s point of view, they all work the same.
This same idea is applied to programming. The power of encapsulated code is that everyone knows how to access it, and in this way can use it regardless of the implementation details and without fear of unexpected side effects.
In Java the basis of encapsulation is the class. A class defines the structure and behavior (data and code) that will be shared by a set of objects. Each object of a given class contains the structure and behavior defined by the class, as if it were stamped out by a mold in the shape of the class. For this reason objects are sometimes referred to as instances of a class. The class is a logical construct, and the object has physical reality.
When you create a class, you will specify the code and data that constitute that class. Collectively, these elements are called members of the class. Specifically, the data defined by the class are referred to as member variables or instance variables. The code that operates on that data is referred to as member methods or just methods. The behavior and interface of a class are defined by the methods that operate on its instance data.
Since the purpose of a class is to encapsulate complexity, there are mechanisms for hiding the complexity of the implementation inside the class. Each method or variable in a class may be marked private or public. The public interface of a class represents everything that external users need to know, or may know. The private members and data can only be accessed by code that is member of the class. As a result, any other code that is not a member of a class cannot access a private method or variable. Since the private members of a class may only be accessed by other parts of your program through the class public methods, you can ensure that no improper actions take place. This means that encapsulation, in a way, extends abstraction.
Inheritance is the process by which one object acquires the properties of another object. This is important because it supports the concept of hierarchical classification. For example, a Golden Retriever is part of the classification dog, which in turn is part of the mammal class, which is under the larger class animal. Without the use of hierarchies, each object would need to define all of its characteristics explicitly. However, by use of inheritance, an object needs only to define those qualities that make it unique within the class. It can inherit general attributes from its parent. Thus it’s the inheritance mechanism that makes it possible for one object to be a specific instance of a more general case.
For example, if you wanted to describe animals in an abstract way, you would say they have some attributes, such as size, intelligence and type of skeletal system. Animals also have certain behavioral aspects: they eat, breathe and sleep. This description of attributes and behavior is the class definition for animals. If you wanted to describe a more specific class of animals, such as mammals, they would have more specific attributes, such as type of teeth and mammary glands. This is known as a subclass of animals, where animals are referred to as mammal’s superclass. Since mammals are simply more precisely specified animals, they inherit all of the attributes from animals. A deeply inherited subclass inherits all of the attributes from each of its ancestors in the class hierarchy.
Inheritance interacts with encapsulation as well. If a given class encapsulates some attributes, then any subclass will have the same attributes plus any that it adds as part of its specialization. This is a key concept that lets object-oriented programs grow in complexity linearly rather than geometrically. A new subclass inherits all of the attributes of all of its ancestors. It does not have unpredictable interactions with the majority of the rest of the code in the system.
Polymorphism is a feature that allows one interface to be used for a general class of action. The specific action is determined by the exact nature of the situation. Consider a stack (which is a last-in, first-out list). You might have a program that requires three types of stacks. One stack is used for integer values, one for floating-point values, and one for characters. The algorithm that implements each stack is the same, even though the data being stored differs. In a non-object-oriented language, you would be required to create three different sets of stack routines, with each set using different names. However, because of polymorphism, in Java you can specify a general set of stack routines that all share the same names.
More generally, the concept of polymorphism is often expressed by the phrase “one interface, multiple methods”. This means that it’s possible to design a generic interface to group of related activities. This helps reduce complexity by allowing the same interface to be used to specify a general class of action. It is the compiler`s job to select the specific action (that is, method) as it applies to each situation. You, the programmer, do not need to make this selection manually. You only need to remember and utilize the general interface.
Extending the dog analogy, a dog’s sense of smell is polymorphic. If the dog smells a cat it will bark and run after it. If the dog smells its food, it will salivate and run to its bowl. The same sense of smell is at work in both situations. The difference is what is being smelled, the type of data being operated upon by the dog’s nose. This same general concept can be implemented in Java as it applies to methods within a Java program.
When properly applied, polymorphism, encapsulation, inheritance and abstraction combine and work together to produce a programming environment that supports the development of far more robust and scalable programs then the process-oriented mode. A well designed hierarchy of classes is the basis for reusing the code in which you have invested time and effort developing and testing. Encapsulation allows you to migrate your implementations over time without breaking the code that depends on the public interface of your classes.
Of the two real world examples, the automobile more completely illustrates the power of object-oriented design. Dogs are fun to think about from an inheritance standpoint, but cars are more like programs. All drivers rely on inheritance to drive different types (subclasses) of vehicles. Whether the vehicle is a school bus, a Mercedes sedan, a Porsche, or a family minivan, drivers can more or less find and operate the steering wheel, the brakes and the accelerator.
People interface with encapsulation features on cars all the time. The brake and gas pedals hide an incredible array of complexity with an interface so simple you can operate them with your feet. The implementation of the engine, the style of brakes, and the size of the tires have no effect on how you interface with the class definition of the pedals.
The final attribute, polymorphism, is clearly reflected in the ability of car manufacturers to offer a wide array of options on basically the same vehicle. For example you can get an anti-lock braking system or traditional brakes, power or rack-and-pinion steering, and 4-, 6- or 8-cylinder engines. Either way, you will still press the brake pedal to stop, turn the steering wheel to change direction, and press the accelerator when you want to move. The same interface can be used to control a number of different implementations.
As you can see, it’s through the application of abstraction, encapsulation, inheritance and polymorphism that the individual parts are transformed into the object known as a car. The same thing applies for computer programs. By the application of object-oriented principles, the various parts of a complex program can be brought together to form a cohesive, robust, maintainable whole.
Source: Herbert Schildt – Java. The Complete Reference. Tenth Edition – 2017.