Introduction to Object-Oriented Programming: Concepts and Principles
Reading: 14 min.
16 May 2023

Introduction to Object-Oriented Programming: Concepts and Principles

Object-oriented programming (OOP) is a popular programming paradigm that provides a structured and modular approach to software development. It allows developers to create complex systems by breaking them down into smaller, manageable components. This article serves as an introduction to the key concepts and principles of object-oriented programming, and explores how they are applied using the PHP programming language.

What is Object-Oriented Programming?

Object-oriented programming is a programming paradigm that focuses on creating objects, which are instances of classes. It revolves around the idea of representing real-world entities as objects, each with its own properties (attributes) and behaviors (methods). Objects interact with each other by sending messages, which trigger specific actions or behaviors.

OOP is built on four main principles:

  1. Encapsulation: Encapsulation is the process of hiding internal implementation details of an object and exposing only the necessary information through well-defined interfaces. It helps in achieving data security and code maintainability.

  2. Inheritance: Inheritance allows objects to inherit properties and behaviors from other objects. It promotes code reuse and allows for the creation of hierarchical relationships between classes. In PHP, classes can inherit from a single parent class using the extends keyword.

  3. Polymorphism: Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enables the use of a single interface to represent multiple forms or types. Polymorphism can be achieved through method overriding or method overloading.

  4. Abstraction: Abstraction involves simplifying complex systems by breaking them down into smaller, more manageable components. It focuses on capturing the essential characteristics and behavior of an object while hiding unnecessary details. Abstract classes and interfaces are used to define abstract types in PHP.

Objects and Classes

In object-oriented programming, objects are instances of classes. A class serves as a blueprint or template for creating objects. It defines the attributes (data members) and behaviors (methods) that an object will have.

Let's take an example of a Car class in PHP:

class Car {
    public $color;
    public $brand;
    
    public function startEngine() {
        echo "Engine started!";
    }
    
    public function drive() {
        echo "Driving the car.";
    }
}

In the above example, the Car class has two attributes: $color and $brand, and two methods: startEngine() and drive(). These attributes and methods define the properties and behaviors of a car object.

To create an object of the Car class and access its properties and methods, we can do the following:

$myCar = new Car();
$myCar->color = "Blue";
$myCar->brand = "Toyota";

$myCar->startEngine(); // Output: Engine started!
$myCar->drive(); // Output: Driving the car.

Encapsulation

Encapsulation is one of the fundamental principles of object-oriented programming. It involves bundling data (attributes) and methods that operate on that data into a single unit called an object. The object encapsulates the internal details and provides an interface through which other objects can interact with it.

By encapsulating the internal details, we achieve data hiding, which prevents direct access to the object's data from external entities. Instead, access to the data is controlled through getter and setter methods, ensuring data integrity and providing a level of abstraction.

Let's enhance our Car class with encapsulation:

class Car {
    private $color;
    private $brand;
    
    public function getColor() {
        return $this->color;
    }
    
    public function setColor($color) {
        $this->color = $color;
    }
    
    public function getBrand() {
        return $this->brand;
    }
    
    public function setBrand($brand) {
        $this->brand = $brand;
    }
    
    public function startEngine() {
        echo "Engine started!";
    }
    
    public function drive() {
        echo "Driving the car.";
    }
}

$myCar = new Car();
$myCar->setColor("Blue");
$myCar->setBrand("Toyota");

echo $myCar->getColor(); // Output: Blue
echo $myCar->getBrand(); // Output: Toyota

$myCar->startEngine(); // Output: Engine started!
$myCar->drive(); // Output: Driving the car.

In the updated Car class, the attributes $color and $brand are now declared as private, preventing direct access from outside the class. Instead, we provide public getter and setter methods (getColor(), setColor(), getBrand(), setBrand()) to interact with the attributes. This way, we maintain control over the access to the object's data.

Inheritance

Inheritance is a powerful feature in object-oriented programming that allows objects to inherit properties and behaviors from other objects. It enables code reuse and promotes the creation of hierarchical relationships between classes.

In PHP, inheritance is achieved using the extends keyword. Let's consider an example to understand how inheritance works:

class Vehicle {
    protected $brand;
    
    public function __construct($brand) {
        $this->brand = $brand;
    }
    
    public function getBrand() {
        return $this->brand;
    }
    
    public function startEngine() {
        echo "Engine started!";
    }
}

class Car extends Vehicle {
    private $color;
    
    public function __construct($brand, $color) {
        parent::__construct($brand);
        $this->color = $color;
    }
    
    public function getColor() {
        return $this->color;
    }
    
    public function drive() {
        echo "Driving the car.";
    }
}

In this example, we have a base class Vehicle that defines the brand attribute and provides methods to access it. The Car class extends the Vehicle class using the extends keyword, thereby inheriting the brand attribute and its associated methods.

The Car class also introduces a new private attribute $color and provides additional methods specific to a car, such as getColor() and drive().

To create an object of the Car class and access its properties and methods, we can do the following:

$myCar = new Car("Toyota", "Blue");
echo $myCar->getBrand(); // Output: Toyota
echo $myCar->getColor(); // Output: Blue
$myCar->startEngine(); // Output: Engine started!
$myCar->drive(); // Output: Driving the car.

By utilizing inheritance, the Car class inherits the getBrand() method from the Vehicle class, and we can access it directly from the Car object.

Polymorphism

Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enables the use of a single interface to represent multiple forms or types.

In PHP, polymorphism can be achieved through method overriding or method overloading. Let's explore both concepts:

Method Overriding:

Method overriding occurs when a subclass provides its own implementation of a method that is already defined in its parent class. The method in the subclass must have the same name, same arguments, and the same or a compatible return type as the method in the parent class.

class Animal {
    public function makeSound() {
        echo "Animal makes a sound.";
    }
}

class Cat extends Animal {
    public function makeSound() {
        echo "Cat meows.";
    }
}

class Dog extends Animal {
    public function makeSound() {
        echo "Dog barks.";
    }
}

$animal = new Animal();
$cat = new Cat();
$dog = new Dog();

$animal->makeSound(); // Output: Animal makes a sound.
$cat->makeSound(); // Output: Cat meows.
$dog->makeSound(); // Output: Dog barks.

In the above example, the Animal class has a method makeSound(). The Cat and Dog classes inherit from Animal and provide their own implementation of the makeSound() method. When the makeSound() method is called on an object of the respective class, the overridden method in that class is executed.

Method Overloading:

Method overloading allows multiple methods with the same name but different parameters to exist in a class. PHP does not natively support method overloading, but it can be simulated using magic methods like __call() or by utilizing optional parameters.

class Calculator {
    public function add($num1, $num2) {
        return $num1 + $num2;
    }
    
    public function addThreeNumbers($num1, $num2, $num3) {
        return $num1 + $num2 + $num3;
    }
}

$calc = new Calculator();
echo $calc->add(5, 10); // Output: 15
echo $calc->addThreeNumbers(5, 10, 15); // Output: 30

In this example, the Calculator class has two methods named add(), but they differ in the number of parameters they accept. By providing different parameter lists, we can achieve similar functionality to method overloading. When calling the methods, PHP automatically resolves the appropriate method based on the number and types of arguments passed.

Abstraction

Abstraction is the process of simplifying complex systems by breaking them down into smaller, more manageable components. It involves capturing the essential characteristics and behavior of an object while hiding unnecessary details.

In PHP, abstraction can be achieved using abstract classes and interfaces.

Abstract Classes:

Abstract classes are classes that cannot be instantiated. They are meant to be extended by other classes. Abstract classes can contain both abstract and non-abstract methods. Abstract methods are declared without an implementation and must be implemented by the concrete (non-abstract) subclasses.

abstract class Shape {
    abstract public function calculateArea();
    
    public function printDetails() {
        echo "This is a shape.";
    }
}

class Rectangle extends Shape {
    private $width;
    private $height;
    
    public function __construct($width, $height) {
        $this->width = $width;
        $this->height = $height;
    }
    
    public function calculateArea() {
        return $this->width * $this->height;
    }
}

$rectangle = new Rectangle(5, 10);
echo $rectangle->calculateArea(); // Output: 50
$rectangle->printDetails(); // Output: This is a shape.

In this example, the Shape class is declared as abstract and contains an abstract method calculateArea(). The Rectangle class extends the Shape class and provides an implementation for the calculateArea() method. The printDetails() method is inherited from the abstract class.

Interfaces:

Interfaces define a contract of methods that a class must implement. Unlike abstract classes, interfaces cannot have any method implementation. A class can implement multiple interfaces, thus supporting multiple contracts.

interface Vehicle {
    public function startEngine();
    public function drive();
}

class Car implements Vehicle {
    public function startEngine() {
        echo "Engine started!";
    }
    
    public function drive() {
        echo "Driving the car.";
    }
}

$car = new Car();
$car->startEngine(); // Output: Engine started!
$car->drive(); // Output: Driving the car.

In this example, the Vehicle interface defines two methods: startEngine() and drive(). The Car class implements the Vehicle interface, thereby enforcing the implementation of these two methods. By implementing the interface, the Car class guarantees that it provides the expected functionality defined by the interface.

PHP Examples

To further illustrate object-oriented programming concepts, let's explore a couple of PHP examples.

Example 1: Bank Account

class BankAccount {
    private $accountNumber;
    private $balance;
    
    public function __construct($accountNumber) {
        $this->accountNumber = $accountNumber;
        $this->balance = 0;
    }
    
    public function deposit($amount) {
        $this->balance += $amount;
    }
    
    public function withdraw($amount) {
        if ($this->balance >= $amount) {
            $this->balance -= $amount;
        } else {
            echo "Insufficient balance.";
        }
    }
    
    public function getBalance() {
        return $this->balance;
    }
}

$account = new BankAccount("123456789");
$account->deposit(1000);
$account->withdraw(500);
echo $account->getBalance(); // Output: 500

In this example, the BankAccount class represents a bank account with features like deposit, withdrawal, and balance inquiry. Each bank account object is created with a unique account number and initialized with a balance of 0. The deposit() and withdraw() methods update the balance based on the given amount, and the getBalance() method returns the current balance.

Example 2: Employee Management System

class Employee {
    private $name;
    private $salary;
    
    public function __construct($name, $salary) {
        $this->name = $name;
        $this->salary = $salary;
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getSalary() {
        return $this->salary;
    }
    
    public function calculateYearlySalary() {
        return $this->salary * 12;
    }
}

class Manager extends Employee {
    private $bonus;
    
    public function __construct($name, $salary, $bonus) {
        parent::__construct($name, $salary);
        $this->bonus = $bonus;
    }
    
    public function getBonus() {
        return $this->bonus;
    }
    
    public function calculateYearlySalary() {
        return ($this->getSalary() + $this->bonus) * 12;
    }
}

$employee = new Employee("John Doe", 5000);
echo $employee->getName(); // Output: John Doe
echo $employee->getSalary(); // Output: 5000
echo $employee->calculateYearlySalary(); // Output: 60000

$manager = new Manager("Jane Smith", 7000, 2000);
echo $manager->getName(); // Output: Jane Smith
echo $manager->getSalary(); // Output: 7000
echo $manager->getBonus(); // Output: 2000
echo $manager->calculateYearlySalary(); // Output: 96000

In this example, the Employee class represents a basic employee with attributes like name and salary. The getName() and getSalary() methods provide access to these attributes, and the calculateYearlySalary() method calculates the yearly salary based on the monthly salary.

The Manager class extends the Employee class and introduces a bonus attribute. It overrides the calculateYearlySalary() method to include the bonus amount in the calculation.

These examples demonstrate how object-oriented programming concepts can be applied to real-world scenarios, providing flexibility and modularity in code design.