# ch02 — メソッドのオーバーライド

Source: https://php-school.pages.dev/topics/class-advanced/ch02-override/
Site: AIと学ぶPHPの学校 (https://php-school.pages.dev)
Lesson: クラスの応用
Level: L3

Reference (official): https://www.php.net/manual/ja/language.oop5.inheritance.php

## 本文

# ch02 — メソッドのオーバーライド

## オーバーライドとは

オーバーライドは **親クラスのメソッドを子クラスで同名再定義し、動作を差し替える** 仕組みだ。

`Notifier::send()` がデフォルトで `"通知を送信しました"` を出すとする。`EmailNotifier extends Notifier` で `send()` を再定義しておけば、`$mail->send()` は `"メールで通知を送信しました"` を出すようになる。継承だけだと親と同じ動きしかできないが、オーバーライドで「同じ呼び方で違う動き」を作れる。

## 書き方

**親と同じ名前・同じ引数構造のメソッドを子クラスに書くだけ**。

```php
class Notifier {
    public function send() {
        echo "通知を送信しました\n";
    }
}
class EmailNotifier extends Notifier {
    public function send() {
        echo "メールで通知を送信しました\n";  // 上書き
    }
}
```

PHP はインスタンスのクラスを見て、子に同名メソッドがあれば子を優先する (動的ディスパッチ)。

## 同じ呼び方で違う動き

`$notifiers = [new EmailNotifier(), new SlackNotifier()];` を `foreach` で回して全部 `send()` を呼べる。**ポリモーフィズム (多態)** と呼ぶ書き方だ。

実務でもメール / Slack / SMS / Push のような複数チャネルを **同じ I/F** で扱いたい場面で頻出する。呼ぶ側はチャネルの違いを気にしなくていい。

## シグネチャの制約

子の引数の型・数と戻り値の型は **親と互換性を持つ必要** がある。PHP 7.4 以降は戻り値の **共変 (covariance)** が許され、親の戻り値型を子で絞れる。

逆 (広げる) や引数を厳しく絞るのは互換性違反になる。まずは「同じシグネチャで上書きする」基本パターンに集中する。実メール送信は Symfony Mailer 等のライブラリを使うが、ここでは `echo` で代用する。

## 学習目標

- 親と同名のメソッドを子で再定義できる
- 子のインスタンスでは子のメソッドが呼ばれる
- 同じ呼び方で通知チャネルごとに違う動きをさせられる

## 所要時間

スライド 4 分 + ドリル 1 問 = **10 分**

## ドリル

| no | 内容 |
|---|---|
| 01 | `Notifier` を継承した `EmailNotifier` で `send()` をオーバーライド |

## 採点

```bash
php scripts/grade.php topics/class-advanced/ch02-override/drill/01-override-method/
```

## 関連チャプター

- [`ch01-inheritance`](../ch01-inheritance/) — 継承 (前提)
- [`ch03-parent-call`](../ch03-parent-call/) — オーバーライドしつつ親も呼ぶ
- [`ch04-abstract`](../ch04-abstract/) — オーバーライドを **強制** する抽象メソッド
- [`ch08-final`](../ch08-final/) — オーバーライドを **禁止** する final

## スライド

---
marp: true
theme: dojo
paginate: true
size: 16:9
---

<!--
LLM_CONTEXT:
  Lesson 9 / Chapter 2
  目的: 親クラスのメソッドを子クラスで同名再定義 (オーバーライド) して動作を差し替える
  題材: 通知システム (Notifier / EmailNotifier / SlackNotifier) で「チャネル別に send() の実装を差し替える」をオーバーライドで表現
  扱わない: parent:: で親メソッド呼び出し (ch03) / abstract (ch04) / interface (ch05) / SMTP や Webhook の本物の通信実装
  読み上げ時間目安: 3 分半〜4 分
-->

# メソッドのオーバーライド

Lesson 9 / Chapter 2

---

## ゴール

通知システムを題材に、

- 親クラス `Notifier` に共通の `send()` を持たせる
- 子クラス `EmailNotifier` / `SlackNotifier` で `send()` を **チャネル別に差し替え** られる
- 同じ呼び方 `$n->send($msg)` で **クラスごとに違う送信先** に届く

ここまでをコードで体験する。

---

## 継承だけだと、親と全く同じ動きになる

```php
<?php
class Notifier {
    public function send(string $message) {
        echo "通知: {$message}\n";
    }
}

class EmailNotifier extends Notifier {
}

$n = new EmailNotifier();
$n->send("登録完了しました");   // 「通知: 登録完了しました」 が出る
```

- でも Email なら **メール本文** として、Slack なら **チャンネル投稿** として送りたい
- 子クラスごとに送信処理を変えたい

---

## 子クラスで同じ名前のメソッドを書く

```php
<?php
class Notifier {
    public function send(string $message) {
        echo "通知: {$message}\n";
    }
}

class EmailNotifier extends Notifier {
    public function send(string $message) {
        echo "[Email] 件名/本文として送信: {$message}\n";
    }
}
```

- 子クラスで親と **同じシグネチャ** のメソッドを定義
- これを **オーバーライド** と呼ぶ

---

## 子のインスタンスでは子のメソッドが呼ばれる

```php
<?php
$n = new EmailNotifier();
$n->send("登録完了しました");
```

実行結果:

```
[Email] 件名/本文として送信: 登録完了しました
```

- `EmailNotifier` には `send()` が定義されているので、親 `Notifier::send()` は使われない
- 親のメソッドは **上書き** された状態になる

---

## 同じ呼び方で、クラスごとに違う動き

```php
<?php
class SlackNotifier extends Notifier {
    public function send(string $message) {
        echo "[Slack] チャンネルに投稿: {$message}\n";
    }
}

class EmailNotifier extends Notifier {
    public function send(string $message) {
        echo "[Email] 件名/本文として送信: {$message}\n";
    }
}

$notifiers = [new EmailNotifier(), new SlackNotifier()];
foreach ($notifiers as $n) {
    $n->send("登録完了しました");
}
```

- 全部 `send()` で呼べる
- 実行されるのは **そのインスタンスのクラスのメソッド**

> 本番では SMTP ライブラリや Slack の Webhook URL を使うが、ここでは echo で雰囲気だけ掴めば OK。

---

## このチャプターでできるようになること

✅ 親と同名のメソッドを子で再定義できる
✅ 子のインスタンスでは子のメソッドが呼ばれる
✅ 同じ呼び方でクラスごとに違う動きをさせられる

→ ドリルへ
