DEV Community

Cover image for What exactly is Object-oriented Programming?
gsbc
gsbc

Posted on • Updated on

What exactly is Object-oriented Programming?

I'm going to be concise here, of course.

First things first, Object-oriented Programming (or OOP, for short), is a programming paradigm that relies on dynamic dispatch, a mechanism that enables polymorphism and code reusability.

OOP organizes code arount the concept of "objects", which are instances of "classes". These classes encapsulate data (attributes) and behaviour (methods) related to a specific entity or a specific concept. Yes, it is abstract. No, it does not have anything to do with Dogs that are Animals and can Swim.

Dynamic dispatch, a.k.a. late binding or runtime method dispatch, is a mechanism in OOP languages that resolves, at runtime, which method implementation to call. The choice is based on the object's actual type, not based on its declared type. This wizardry allows objects of different classes to be considered objects of a common superclass.

There is some correlation between dynamic dispatch and OOP, summarized as follows:

  • Encapsulation: dynamic dispatch allows objects to have their own implementation of a method, hidden from the outside world;
  • Inheritance: dynamic dispatch supports inheritance, enabling derived classes to override or extend methods from their parent classes;
  • Polymorphism: dynamic dispatch is a enabler of polymorphism, as it allows a single method name to be associated with multiple implementations, depending on the object's type at runtime, promoting code reusability and modular, clean design;
  • Abstraction: dynamic dispatch enables abstraction by allowing programmers to define a common interface (abstract class or interface) that multiple classes can implement, while the actual details of the implementation are resolved at runtime.

At runtime, the appropriate method implementation is selected based on the object's actual type. Here's an example using interfaces (a contract that implementing classes must adhere to). This promotes code reusability, modularity and separation of concerns:

// Define the Shape interface
interface Shape {
    double getArea();
}

// Define a Circle class that implements Shape
class Circle implements Shape {
    private double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

// Define a Rectangle class that implements Shape
class Rectangle implements Shape {
    private double width;
    private double height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double getArea() {
        return width * height;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape[] shapes = new Shape[3];
        shapes[0] = new Circle(5);
        shapes[1] = new Rectangle(4, 6);
        shapes[2] = new Circle(3);

        for (Shape shape : shapes) {
            System.out.println("Area: " + shape.getArea());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In the example above, a Shape interface is defined with a single method: getArea(). The Circle and Rectangle classes implement the Shape interface and provide their own implementations of the interface's contract method getArea().

I'm not giving an example of dynamic dispatch using inheritance because this concept promotes coupling, which is overall not desirable.

Hope you people enjoyed it, any feedback is appreciated.

Next up: inversion of control (IoC) and dependency injection.

Top comments (0)