objects namespaces and application architecture

Inheritance

Inheritance lets one class extend another class. The child class receives the parent's public and protected behaviour and can add or override behaviour.

Inheritance is useful when there is a true "is a kind of" relationship and the child can safely be used wherever the parent is expected. It is risky when it is used only to share code, because parent and child classes can become tightly coupled.

Extend A Parent Class

Use extends to create a child class.

PHP example
<?php

declare(strict_types=1);

class Notification
{
    public function summary(): string
    {
        return 'Notification ready';
    }
}

class EmailNotification extends Notification
{
    public function recipientType(): string
    {
        return 'email';
    }
}

$notification = new EmailNotification();

echo $notification->summary() . PHP_EOL;
echo $notification->recipientType() . PHP_EOL;

// Prints:
// Notification ready
// email

EmailNotification can call the inherited summary() method because it extends Notification.

Override Behaviour

A child class can replace a parent method with its own implementation.

PHP example
<?php

declare(strict_types=1);

class Report
{
    public function title(): string
    {
        return 'Report';
    }
}

class SalesReport extends Report
{
    public function title(): string
    {
        return 'Sales Report';
    }
}

$report = new SalesReport();

echo $report->title() . PHP_EOL;

// Prints:
// Sales Report

Overriding is powerful, but it means callers may get different behaviour depending on the concrete child class.

Call Parent Behaviour

Use parent:: when a child wants to reuse part of the parent's method.

PHP example
<?php

declare(strict_types=1);

class BaseEmail
{
    public function subject(): string
    {
        return 'Account update';
    }
}

class SecurityEmail extends BaseEmail
{
    public function subject(): string
    {
        return '[Security] ' . parent::subject();
    }
}

$email = new SecurityEmail();

echo $email->subject() . PHP_EOL;

// Prints:
// [Security] Account update

This keeps common behaviour in one place while still allowing a child to specialise it.

Child Objects Can Be Used As Parent Objects

Code that accepts a parent type can receive a child object.

PHP example
<?php

declare(strict_types=1);

class Message
{
    public function body(): string
    {
        return 'Message body';
    }
}

class WelcomeMessage extends Message
{
    public function body(): string
    {
        return 'Welcome to the application';
    }
}

function printMessage(Message $message): void
{
    echo $message->body() . PHP_EOL;
}

printMessage(new WelcomeMessage());

// Prints:
// Welcome to the application

This only works well when the child honours the expectations of the parent type.

Avoid Inheritance For Simple Code Sharing

If two classes merely need the same helper, inheritance may be the wrong tool.

PHP example
<?php

declare(strict_types=1);

class MoneyFormatter
{
    public function formatPennies(int $pennies): string
    {
        return '£' . number_format($pennies / 100, 2);
    }
}

$formatter = new MoneyFormatter();

echo $formatter->formatPennies(2499) . PHP_EOL;

// Prints:
// £24.99

Passing a helper object is often clearer than making unrelated classes extend a shared base class.

Watch Fragile Base Classes

Changes to a parent class can unexpectedly affect every child class. This is why deep inheritance trees are hard to maintain.

PHP example
<?php

declare(strict_types=1);

class Importer
{
    public function sourceType(): string
    {
        return 'generic';
    }
}

class CsvImporter extends Importer
{
    public function sourceType(): string
    {
        return 'csv';
    }
}

echo (new CsvImporter())->sourceType() . PHP_EOL;

// Prints:
// csv

Keep inheritance shallow and purposeful.

What To Remember

Inheritance models "is a kind of" relationships. Use it when child objects can safely stand in for parent objects. Prefer composition when the goal is sharing services, helpers, or changeable behaviour.

Practice

Task: Specialise A Notification

Create a parent notification class and a specialised child class.

Requirements

  • Use declare(strict_types=1);.
  • Create a Notification parent class.
  • Give it a channel() method that returns generic.
  • Give it a summary() method.
  • Create an EmailNotification child class.
  • Override channel() so it returns email.
  • Use a function that accepts Notification and prints its summary and channel.
  • Pass an EmailNotification into that function.
  • Include the expected output as comments in the same PHP code block.

The example should show that a child object can be used where the parent type is expected.

Show solution
PHP example
<?php

declare(strict_types=1);

class Notification
{
    public function channel(): string
    {
        return 'generic';
    }

    public function summary(): string
    {
        return 'Notification ready';
    }
}

class EmailNotification extends Notification
{
    public function channel(): string
    {
        return 'email';
    }
}

function printNotification(Notification $notification): void
{
    echo $notification->summary() . ' via ' . $notification->channel() . PHP_EOL;
}

printNotification(new EmailNotification());

// Prints:
// Notification ready via email

EmailNotification extends Notification, so it can be passed to a function that asks for Notification. The child class specialises the channel without changing the function signature.