From Procedural to Object Oriented Programming - Part 2

This is the second part of posts about transitioning from procedural to object oriented programming paradigm.

In this post we will take the basic calculator script that we wrote in a procedural way in the previous post and we will transition it to object oriented programming paradigm. You can find this script at my github repository.

Objective

Improve code quality and readability by transforming our procedural code to object oriented code. The script's functionality will stay the same it will still be a CLI Calculator script which handles adding, subtracting, multiplying and division.

Start of our script

Create a php file named: object-oriented-calculator.php. With the following contents:

<?php declare(strict_types=1);

if (php_sapi_name() !== 'cli') {
    die('This script can be called only from the command line.' . PHP_EOL);
}

This step is basically the same as the one in the previous post, the difference is that we are not handling count of the provided arguments because we will do that in the constructor. Check for the CLI Interface stays the same because we want to ensure that nobody can call it from a web server.

Use of php_sapi_name() function is explained in the previous post.

Creating our Calculator class

Skeleton of our class:

class Calculator
{
    /**
     * @var string
     */
    private $rawArgument;

    /**
     * @var int
     */
    private $firstNum;

    /**
     * @var int
     */
    private $secondNum;

    /**
     * @var string
     */
    private $operator;

    /**
     * @var array
     */
    private $allowedOperators = [
        '+',
        '-',
        '*',
        '/'
    ];

    public function __construct(array $argv)
    {
        if (count($argv) <= 1) {
            throw new InvalidArgumentException('You need to provide an operation!');
        }

        $this->input = $argv[1];
    }
}

We have defined a class named Calculator. This class also holds properties such as $rawArgument or $allowedOperators. These properties can be only used within this class, because they are defined as private visibility in PHP.

There are three types of properties visibility within a class. First we have public visibility which can be used by any class, then we have protected visibility which can be only used in classes that extends this class and lastly a private visibility which can be only used within the class that you defined these properties.

At the end we defined a method (which is still named function, but in Object oriented programming it is called a method) which is called __construct. This is a special method in PHP which get's called automatically when we create an object (eg.: new Calculator($argv)). We accept one argument of type array and we immediately check if this array has one or less element in it, if it does we throw an exception because it did not satisfy our requirement. If this array has more than one element we assign it to our $this->input property which we can use elsewhere in our class.

Methods starting with two underscores __construct, __invoke, etc. are called magic methods.

Define methods for calculation operations

private function add(int $a, int $b): int
{
    return $a + $b;
}

private function subtract(int $a, int $b): int
{
    return $a - $b;
}

private function multiply(int $a, int $b): int
{
    return $a * $b;
}

private function divide(int $a, int $b): float
{
    return $a / $b;
}

The difference is very subtle we only added visibility to our methods if we compare it to the procedural way.

Parse the input and return result

We defined three methods, and they will use the fluent interface where we chain methods sequentially.

private function findOperator(): self
{
    foreach ($this->allowedOperators as $operator) {
        if (strpos($this->input, $operator)) {
            $this->operator = $operator;
            return $this;
        }
    }

    throw new InvalidArgumentException('Unrecognized operand provided!');
}

private function parseInput(): self
{
    [$this->firstNum, $this->secondNum] = explode($this->operator, $this->input);

    if (is_numeric($this->firstNum) && is_numeric($this->secondNum)) {
        return $this;
    }

    throw new InvalidArgumentException('Provided arguments are not of type integer.');
}

private function getResult(): float
{
    switch ($this->operator) {
        case '+':
            $result = $this->add((int)$this->firstNum, (int)$this->secondNum);
            break;
        case '-':
            $result = $this->subtract((int)$this->firstNum, (int)$this->secondNum);
            break;
        case '*':
            $result = $this->multiply((int)$this->firstNum, (int)$this->secondNum);
            break;
        case '/':
            $result = $this->divide((int)$this->firstNum, (int)$this->secondNum);
            break;
    }

    return $result;
}

Method findOperator iterates over the $this->allowedOperators array and checks if the input provided by the user is valid. If the user provides a valid operation this method will return itself back otherwise it will throw an exception. Next method parseInput seperates the user input into $this->firstNum and $this->secondNum and returns itself back otherwise it throws an exception if these properties are not numerical. The getResult method just switches between operations and calls the correct method to provide the result.

Last step

We can use the fluent interface now and wrap it in a calculate method:

public function calculate(): float
{
    return $this->findOperator()
        ->parseInput()
        ->getResult();
}

Constructing an object and calling calculate method

The only thing that is now left is constructing our Calculator class and calling the calculate method. Define this part outside of the class Calculator block:

try {
    $calculator = new Calculator($argv);
    printf('Result: %s' . PHP_EOL, $calculator->calculate());
} catch (Throwable $e) {
    printf('Error: %s' . PHP_EOL, $e->getMessage());
}

We provide $argv as our argument to the __construct magic method and then call the `$calculator->calculate() method. If the provided input was correct we return a result otherwise we catch an exception that we threw and display it's message. Call this script like so: php object-oriented-calculator.php 10/5. The same goes for the other operations.

Conclusion

With this post we have finished transitioning our code from procedural paradigm to object oriented paradigm. This was an introduction to the object oriented paradigm in future posts we will take a deeper look at object oriented programming.