Saturday, September 21, 2024

The most Powerful Concepts in Programming Languages: Object-oriented Paradigm



In this blog, I want to present the concepts that I have found to be the strongest in programming languages, and if you master them, you have solid coding thinking. I will explain them individually by providing examples in Java and Python. In this article, I will explain the first one (Object-oriented) and present the others in future posts.


1. Object-oriented

2. Recursion
3. Concurrency
4. Functional Programming and Lambda Expressions
5. Data Structure
6. Software Design Patterns
7. Annotations/Decorators
8. Machine Learning



1. What is Object-oriented ?

Object-oriented is one of the most powerful concepts that has revolutionized programming languages. It allows you to create software similar to real entities by capturing their behavior and interactions with each other.


Object-oriented is a programming paradigm that aims to structure code as reusable components called objects. These objects represent real-world entities and encapsulate static (data) and dynamic behavior. Data are represented by fields or properties. In other words, they are constants and variables with different scopes, e.g. private or public. The values of an object’s fields at a given instant constitute its state. The behavior of an object is the change in its state (i.e, the value of its variables). That is possible thanks to the execution of its methods. 


A method is a function with arguments that, once called, is executed within the object itself or from another object. When an object invokes a method of another object, we call this action ‘message passing’. That implements the interactions between objects.


An example of an object is an employee with properties such as name or salary. These fields represent the object employee’s data. When an employee starts work, an ‘employee’ object can be created, and when he quits, its representative object is destroyed.


As you notice, many employees have the same properties and are subject to the same action. Therefore, we can group them in a collection called a class. A class is a template for defining the common properties and methods of all objects in that class.


The software in the object-oriented paradigm is a collection of self-contained modules that interact with each other. The module itself is composed of classes.


The advantages of object-oriented are multiple. It promotes well-organized, self-contained code, modular, and reusable code. That is helpful in developing and maintaining complex software. In object-oriented systems, many developers can work on the same project in parallel and collaborate easily with each other. That helps speed up the development process and reduce its costs. In the next subsections, I introduce a brief description of the main concepts of the object-oriented paradigm.


1.1. Encapsulation

When using an object-oriented library, all you need to know is the signature of the class or its methods and what they do. You don’t need to know the implementation details of such a class or its methods. That is called encapsulation and aims to hide details and complexities. The object-oriented paradigm exposes only the necessary class interface and its methods so that you can use it without getting bogged down in implementation details that remain hidden from you.


1.2. Inheritance

It is a mechanism that allows a class (called sub-class in this case) to inherit properties and behaviors from another class, called the super-class or base class. Inheritance promotes code reuse because it implements ‘is-a’ relationship for more naturally structured code. Inheritance is a relationship between classes. For instance, if you have classes like ‘Dog’ or ‘Cat’, you realize that they are both animals. You can create an ‘Animal’ class to encapsulate common properties and methods between them, such as size, color, eating, or breathing.


1.3. Abstraction

Encapsulation and inheritance promote the concept of abstraction. By hiding unnecessary implementation details and revealing the essential characteristics of objects, the code becomes more readable and reusable. Note that classes’ interfaces are defined independently of their implementations, which means you can always think abstractly. You can first define the classes and then group common behavior between certain classes to get abstract classes.


For example, all employees have general properties such as names and dates of birth. These properties are not relevant to the ‘Employee’ class but rather to the ‘Person’ class. You can extract an abstract ‘Person’ class to wrap the generic behavior of all employees. Employees can be divided into different groups like managers, secretaries, or software developers. You can keep specialized classes, from the most abstract to the most concrete, to form a class hierarchy. This decomposition of your software system generates small manageable and understandable components.


1.4. Association

An association is a relationship between classes that allows objects to interact with each other. In an association, objects exchange information and collaborate to achieve an overall goal. Associations can be of different types such as one-to-one, one-to-many, or many-to-many. An example of a one-to-many relationship is that an employee can have multiple accounts or addresses.


1.5. Composition

It implements ‘has-a’ relationship and it is a relationship between objects. A composition is an association when an object owns another one. For instance, a car has wheels.


1.6. Polymorphism

Polymorphism comes from two Greek words, ‘Poly’ which means numerous, and ‘Morphs’ which means forms. Polymorphism is the ability of an object to respond in different ways to different situations. For example, the method ‘Equal’ which returns true or false whether two objects are equal or not, is a polymorph method. It acts on all types of the two objects. 


2. Example of OO in Java

In this section, we create a small example to illustrate the concept of object-oriented with inheritance and polymorphism. The example concerns certain classes ‘Shape’, ‘Rectangle’, and ‘Circle’. As you can see, the ‘Rectangle’ and ‘Circle’ classes are specific shapes. In other words, the ‘Shape’ class is the super-class of ‘Rectangle’ and ‘Circle’ and will encompass the common properties and behaviors of the circle, rectangle, and any other shape. Let’s imagine some code for this class:


public class Shape {

    protected String name;

    public Shape(String name) {

        this.name = name;

    }

    public double getArea() {

        return 0.0;

    }

    public double getPerimeter(){

        return 0.0;

    }

    public void showInfo() {

        System.out.println("Shape Type: Shape");

        System.out.println("Name: " + name);

    }

}


According to this code, each shape has a name declared as protected to be visible at the level of its sub-classes. Each shape has an area and a perimeter. For this reason, we created two methods ‘getArea’ and ‘getPerimeter’ to calculate them respectively. But inside the code of a shape, we don’t know how to do it. So we just return 0.0. We also declare a method that helps us display some information about the shape. We now create the ‘Rectangle’ and ‘Circle’ sub-classes that inherit from the ‘Shape’ class:


public class Rectangle extends Shape {

    private double width;

    private double height;

    public Rectangle(String name, double width, double height) {

        super(name);

        this.width = width;

        this.height = height;

    }

    @Override

    public double getArea() {

        return width * height;

    }

    @Override

    public double getPerimeter(){

        return 2*(width+height);

    }

    @Override

    public void showInfo() {

        System.out.println("Shape Type: Rectangle");

        System.out.println("Name: " + name);

        System.out.println("Width: " + width);

        System.out.println("Height: " + height);

        System.out.println("Area: " + getArea());

        System.out.println("Perimeter: " + getPerimeter());

    }

}


public class Circle extends Shape{

    private double radius;


    public Circle(String name, double radius) {

        super(name);

        this.radius = radius;

    }


    @Override

    public double getArea() {

        return Math.PI * radius * radius;

    }


    @Override

    public double getPerimeter(){

        return 2*Math.PI*radius;

    }


    @Override

    public void showInfo() {

        System.out.println("Shape Type: Circle");

        System.out.println("Name: " + name);

        System.out.println("Radius: " + radius);

        System.out.println("Area: " + getArea());

        System.out.println("Perimeter: " + getPerimeter());

    }

}


With the ‘extends’ keyword, the ‘Rectangle’ and ‘Circle’ classes inherit from the ‘Shape’ class. This inheritance allows them to have the ‘name’ field as their own property and they can add new attributes according to their specificity. In the ‘Rectangle’ class, we add the ‘width’ and ‘height’ fields, because a rectangle has these two dimensions. It’s now possible to calculate the area and perimeter of a rectangle using its newly added fields. 

In the ‘Rectangle’ class, we override the methods ‘getArea’ and ‘getPerimeter’ with new code to effectively calculate the area and perimeter of a rectangle. We also override the method ‘showInfo’ because we have more information about the ‘rectangle’ object. The same logic is applied to the circle. The ‘Circle’ class extends the ‘Shape’ class with the property ‘radius’ and overrides the methods ‘getArea’, ‘getPerimeter’, and ‘showInfo’ to provide its specific implementation.


We now create a class named ‘OODemo’ to instantiate the ‘Rectangle’ and ‘Circle’ classes and call their methods. 


public class OODemo {

    public static void main(String[] args) {

        Shape rectangle = new Rectangle("My Rectangle", 5.0, 7.0);

        Shape circle = new Circle("My Circle", 6.0);

        System.out.println("Rectangle Info:");

        rectangle.showInfo();

        System.out.println();

        System.out.println("Circle Info:");

        circle.showInfo();

    }

}


As you notice, the ‘rectangle’ and ‘circle’ instances are both declared to be ‘shape’. Bhowever, they are instantiated by using their own constructors to enable access to newly defined properties and methods in their ‘Rectangle’ and ‘Circle’ classes. The ‘rectangle’ instance will call its own ‘showInfo’ implementation, just like the ‘circle’ instance. Depending on each type, the appropriate ‘showInfo’ method will be called as well as the ‘getArea’ and ‘getPerimeter’ methods. Therefore, these methods are polymorph. 


The output after executing the main method of the class ‘OODemo’ displays the properties as well as the calculated area and perimeter for the rectangle and circle:


Rectangle Info:

Shape Type: Rectangle

Name: My Rectangle

Width: 5.0

Height: 7.0

Area: 35.0

Perimeter: 24.0


Circle Info:

Shape Type: Circle

Name: My Circle

Radius: 6.0

Area: 113.09733552923255

Perimeter: 37.69911184307752


3. Example of OO in Python

Python supports extensively the concepts of object-oriented and is considered an object-oriented programming language. You can start using object-oriented concepts in Python by defining classes with the ‘class’ keyword. A class in Python allows the encapsulation of attributes and methods that update the attributes. Let’s illustrate that through an example. 


We can take the same example described above for Java to show how Python implements object-oriented concepts. For that, we need to import the ‘math’ module to access the value of PI (3.14). Moreover, we import from the ‘abc’ module, the abstract class ‘ABC’, and the annotation ‘abstractmethod’.


Python provides such elements to permit you to define abstract classes that can also have abstract methods. Abstract methods should be overridden in sub-classes to be executed. The expression ‘Shape(ABC)’ means that the ‘Shape’ class inherits from the ‘ABC’ class. You have surely noticed this ‘__init__’ method which aims to initialize a newly created object.


import math

from abc import ABC, abstractmethod


class Shape(ABC):

    def __init__(self, name):

        self.name = name


    @abstractmethod

    def calc_area(self):

        pass


    @abstractmethod

    def calc_perimeter(self):

        pass


    def show_info(self):

        print("Shape Type: Shape")

        print("Name: " + self.name)


We create another class ‘Rectangle’ which inherits from the ‘Shape’ class. The ‘Rectangle’ class extends ‘Shape’ with new attributes (‘width’, ‘height’) and overrides the other methods (‘calc_area’, ‘calc_perimeter’, and ‘show_info’).


class Rectangle(Shape):

    def __init__(self, name, width, height):

        super().__init__(name)

        self.width = width

        self.height = height


    def calc_area(self):

        return self.width * self.height


    def calc_perimeter(self):

        return 2 * (self.width + self.height)


    def show_info(self):

        print("Shape Type: Rectangle")

        print("Name: " + self.name)

        print("Width: " + str(self.width))

        print("Height: " + str(self.height))

        print("Area: " + str(self.calc_area()))

        print("Perimeter: " + str(self.calc_perimeter()))


We can also create another subclass ‘Circle’ of the ‘Shape’ class which will define its own attributes like ‘radius’ and override the ‘calc_area’, ‘calc_perimeter’, and ‘show_info’ methods as follows:


class Circle(Shape):

    def __init__(self, name, radius):

        super().__init__(name)

        self.radius = radius


    def calc_area(self):

        return math.pi * self.radius ** 2


    def calc_perimeter(self):

        return 2 * math.pi * self.radius


    def show_info(self):

        print("Shape Type: Circle")

        print("Name: " + self.name)

        print("Radius: " + str(self.radius))

        print("Area: " + str(self.calc_area()))

        print("Perimeter: " + str(self.calc_perimeter()))


Like in Java code, these methods are also polymorph in Python. To see this, let’s create an object of the ‘Rectangle’ class and another of the ‘Circle’ class in Python as follows: 


rectangle = Rectangle("My Rectangle", 5.0, 7.0)

circle = Circle("My Circle", 6.0)

print("Rectangle Info:")

rectangle.show_info()

print("")

print("Circle Info:")

circle.show_info()


We called the method ‘show_info()’ of the ‘rectangle’ and ‘circle’ instances to obtain the following result:


Rectangle Info:

Shape Type: Rectangle

Name: My Rectangle

Width: 5.0

Height: 7.0

Area: 35.0

Perimeter: 24.0


Circle Info:

Shape Type: Circle

Name: My Circle

Radius: 6.0

Area: 113.09733552923255

Perimeter: 37.69911184307752


Each instance will call its own implementation of one of the three methods depending on its needs.




No comments:

Post a Comment

Blog Posts

Enhancing Performance of Java-Web Applications

Applications built with a Java back-end, a relational database (such as Oracle or MySQL), and a JavaScript-based front-end form a common and...