PHP

PHPのクラス・継承・インターフェース入門

PHPのクラスを起点に、継承とインターフェースの役割や使い分けを、初学者向けの例で整理します。保守性と再利用性を高める設計の考え方を学びます。

この記事でわかること

  • クラスでデータと処理をひとまとまりにする考え方が分かる
  • 継承による共通化と、インターフェースによる契約の違いを理解できる
  • 継承やインターフェースを使う場面と、使いすぎを避ける判断軸を持てる

PHPのクラスとは何か

クラスは、データを表すプロパティと、そのデータを操作するメソッドをまとめた設計図です。クラスから「インスタンス」と呼ばれる実体を作り、メソッドを呼び出して利用します。PHPでは、クラスを class で定義し、new でインスタンスを生成します。

PHP
<?php
class User
{
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function greet(): string
    {
        return "こんにちは、{$this->name}さん";
    }
}

$user = new User('佐藤');
echo $user->greet();

// 期待される表示:
// こんにちは、佐藤さん

$name を private にすると、クラスの外部から直接変更されないようにできます。外部から必要な操作だけを public メソッドとして公開することで、データの扱いをクラス内に集約できます。

継承で共通処理を再利用する

継承では、親クラスの public や protected のプロパティ・メソッドなどを子クラスが引き継ぎます。子クラスは親の処理をそのまま利用でき、必要であればメソッドを上書きして固有の処理を追加できます。

PHP
<?php
class Animal
{
    public function speak(): string
    {
        return '音を出します';
    }
}

class Dog extends Animal
{
    public function speak(): string
    {
        return 'ワンワン';
    }
}

$dog = new Dog();
echo $dog->speak();

// 期待される表示:
// ワンワン

継承は共通処理の再利用に向いています。一方で、親クラスと子クラスの関係が強くなるため、親クラスの変更が複数の子クラスへ影響することがあります。単にコードを短くする目的ではなく、「子は親の一種」と説明できる関係かを確認しましょう。

インターフェースで実装の契約を定義する

インターフェースは、クラスが実装すべきメソッドを定める契約です。具体的な処理は持たず、実装クラスが implements で指定します。インターフェースのメソッドを実装しないクラスは、契約を満たせません。

PHP
<?php
interface Notifier
{
    public function send(string $message): string;
}

class EmailNotifier implements Notifier
{
    public function send(string $message): string
    {
        return "メール送信: {$message}";
    }
}

class LogNotifier implements Notifier
{
    public function send(string $message): string
    {
        return "ログ記録: {$message}";
    }
}

function notifyUser(Notifier $notifier): string
{
    return $notifier->send('注文を受け付けました');
}

echo notifyUser(new EmailNotifier());

// 実装を LogNotifier に差し替えても、
// notifyUser() の処理は変更しません。

呼び出し側が Notifier という契約だけに依存すれば、EmailNotifier や LogNotifier のような具体的なクラスを差し替えやすくなります。実装の詳細を分離できるため、テスト用のクラスを用意する場合にも役立ちます。

継承とインターフェースを使い分ける

観点継承インターフェース
共有するもの親クラスの処理やデータ実装すべきメソッドの契約
関係性子が親の一種である異なるクラスが同じ役割を持つ
実装の自由度親クラスの設計に影響されやすい各クラスが自由に処理を実装できる
主な用途共通処理の再利用実装の差し替えや依存関係の分離
継承とインターフェースの違い

共通の実装を受け継ぎたい場合は継承を検討します。共通の約束だけを定め、複数の実装を交換できるようにしたい場合はインターフェースが適しています。小さな処理まで無理に親クラスへ集約せず、将来の変更やテストのしやすさを基準に選びましょう。

クラス・継承・インターフェースを組み合わせる

実際の設計では、インターフェースで共通の操作を定義し、必要に応じて親クラスへ共通処理を切り出します。呼び出し側はインターフェース型を受け取ることで、具体的な実装クラスの違いを意識せずに済みます。

PHP
<?php
interface ReportExporter
{
    public function export(string $title): string;
}

class BaseExporter
{
    protected function formatTitle(string $title): string
    {
        return "[REPORT] {$title}";
    }
}

class TextExporter extends BaseExporter implements ReportExporter
{
    public function export(string $title): string
    {
        return $this->formatTitle($title);
    }
}

function createReport(ReportExporter $exporter): string
{
    return $exporter->export('売上レポート');
}

echo createReport(new TextExporter());

// 期待される表示:
// [REPORT] 売上レポート

保守しやすいPHPコードにするための注意点

  • 親クラスに責務を詰め込みすぎず、クラスごとの役割を明確にする。
  • インターフェースを増やしすぎず、複数の実装で本当に共有したい契約だけを定義する。
  • メソッド名や引数の役割を揃え、利用側が実装の違いを意識しなくて済む形を目指す。
  • 仕様変更時の影響範囲を確認し、必要なら継承より委譲も検討する。

参考