Programming is an exercise in abstraction. If a program is intended to solve some problem, we must first be able to model this problem in an abstract way, in order to express it in a programming language. Only after the problem is modeled the solution can be built.
An algorithm can be seen as an abstract solution for a category of problems. For example, the same sorting algorithms can be used to sort anything: numbers, names or dates.
Thus, we can talk about levels of abstraction: In the first level, we have the problem of sorting a list of values, and we need to abstract this problem in order to write a solution (a program) for it. In a higher level of abstraction, we are able to understand that the solution is independent of the type of values being sorted, and then we can create a sorting algorithm. When we create an algorithm, we are generalizing the solution.
Design Patterns are another example of abstractions. In this case, experienced software developers were able to identify recurring designs in the systems they have built, express these designs in an abstract way, and classify them according to the scenarios in which they should be used. When a programmer decides to apply a pattern, he needs to adapt the abstract design to the entities in his specific system. When we use a design pattern, we are specializing an abstract design.
Therefore, the logic of abstraction can be used in both directions:
- Generalization: Creating a more abstract solution by ignoring details.
- Specialization: Creating a less abstract solution by adding details.
In object-oriented programming, abstraction is naturally expressed by inheritance. In a well-structured class hierarchy, each superclass is a generalization of its subclasses, and each subclass is a specialization of its superclass.
Sometimes we start with a set of concrete classes and then we understand that they have something in common, despite their differences. In this case we abstract their properties, preserving the shared ones and removing the specific ones, and the result is a superclass that generalizes all the previous classes.
In other situations we start with a single concrete class and then understand that there are several possible ways to extend its functionality. In this case we specialize the initial class by creating new concrete subclasses, each with its own properties and behavior, but all of them sharing the functionality defined in the superclass.
Without a mechanism such as inheritance we would be severely limited in our capacity to model a problem and its solution. Programming languages without inheritance lack expressive power.
Accordingly, programmers that don’t know how to make good use of inheritance are less effective. Invest your time in mastering the correct usage of abstraction, and you will get very concrete quality improvements!