# ch04 — 抽象クラス (abstract)

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

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

## 本文

# ch04 — 抽象クラス (abstract)

## なぜ abstract が要るか

EC サイトには「カード払い」「銀行振込」「コンビニ払い」と複数の決済手段があるが、どれも **「金額を受け取って結果を返す」** 枠は揃えたい。親で枠だけ決めて、具体の処理は子に必ず書かせたい。これが `abstract` の出番。

## abstract の書き方

`abstract class` でクラス自体を抽象に、`abstract function` で **中身を書かないメソッド** を宣言する。

```php
abstract class PaymentMethod {
    abstract public function charge(int $amount): string;
}
```

`PaymentMethod` は **そのままでは `new` できない**。継承して使う。

## 子に実装を強制する

抽象メソッドを持つクラスを継承した子は、**すべての抽象メソッドを実装しないと自分も abstract のまま** になる (= `new` できずエラー)。

```php
class Card extends PaymentMethod {
    public function charge(int $amount): string {
        return "カードで {$amount} 円を決済しました";
    }
}
```

`charge()` の実装漏れを **コードを書いた時点で防げる**。

## abstract vs interface

| 使い分け | 抽象クラス | インターフェース |
|---|---|---|
| 共通の実装も親に書きたい | **○** | × |
| 契約 (メソッド名) だけ揃えたい | △ | **○** |
| 多重継承的に複数の契約を持たせたい | × | **○** |

共通の処理を親に持たせたいなら `abstract`、契約だけならインターフェース (次章)。

## 学習目標

- `abstract class` は **インスタンス化できない** ことを理解する (`new PaymentMethod()` は不可)
- `abstract function` は **中身を書かず子で必ず実装** させる仕組み
- 「決済手段の共通の枠は親、具体の決済処理は子で必ず書く」を強制できる

## 所要時間

スライド 5 分 + ドリル 1 問 = **12 分**

## ドリル

| no | 内容 |
|---|---|
| 01 | `abstract class PaymentMethod` を継承した `Card` で `charge()` を実装する |

## 採点

```bash
php scripts/grade.php topics/class-advanced/ch04-abstract/drill/01-abstract-payment/
```

## 関連チャプター

- [`ch01-inheritance`](../ch01-inheritance/) — 継承 (前提)
- [`ch02-override`](../ch02-override/) — オーバーライド
- [`ch05-interface`](../ch05-interface/) — 契約だけのインターフェース

## スライド

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

<!--
LLM_CONTEXT:
  Lesson 7 / Chapter 4
  目的: abstract class と abstract method で「枠は親が決め、実装は子に強制する」設計を組める
  題材: 決済手段 (PaymentMethod) — EC / Web 案件で頻出する「複数の支払い方法を同じ呼び出し方で扱う」場面
  扱わない: interface (ch05) / テンプレートメソッドの応用例 / 3D Secure などの決済プロトコル詳細
  読み上げ時間目安: 5 分
-->

# 抽象クラス (abstract)

Lesson 7 / Chapter 4

---

## ゴール

```php
abstract class PaymentMethod {
    abstract public function charge(int $amount): bool;
}
```

- `abstract class` を **そのままは new できない** ことが分かる
- 子クラスに `charge()` の実装を強制できる
- 「枠は親、具体は子」という設計が書ける

---

## 抽象クラスとは

```
共通の枠だけは親で決める。
中身は子クラスで必ず書きなさい。
```

- 抽象クラス = そのままではインスタンス化できないクラス
- 必ず継承して、抽象メソッドを実装した子クラスを経由して使う
- 「決済を実行できる支払い手段」など **抽象的な概念だけ** を親に置く

---

## 書き方

```php
<?php
abstract class PaymentMethod {
    abstract public function charge(int $amount): bool;
}
```

- `abstract class` でクラス自体を抽象に
- `abstract function` で中身を書かないメソッド宣言
- 本体 `{ ... }` を持たず、 `;` で終わる

---

## new できない

```php
<?php
$p = new PaymentMethod();
// ❌ Fatal error: Cannot instantiate abstract class PaymentMethod
```

- 抽象クラスは直接 `new` できない
- 「決済できる支払い手段」は具体形 (Card / BankTransfer) があってこそ意味を持つ
- 必ず子クラスを作って使う

---

## 子クラスで実装する

```php
<?php
class Card extends PaymentMethod {
    public function __construct(private string $cardNumber) {}

    public function charge(int $amount): bool {
        // 実務では決済 SDK を呼ぶ。ここでは成功したことにする
        echo "カード {$this->cardNumber} で {$amount} 円決済\n";
        return true;
    }
}

$c = new Card('4242-4242-4242-4242');
$c->charge(1500);  // カード 4242-... で 1500 円決済
```

- 子クラスは **抽象メソッドを必ず実装**
- 実装し忘れると「子クラスもまだ抽象だ」とエラーになる
- 本番ではカード番号を直接持たず、決済 SDK のトークンを使う

---

## 別の子クラスも同じ枠で書ける

```php
<?php
class BankTransfer extends PaymentMethod {
    public function charge(int $amount): bool {
        echo "銀行振込で {$amount} 円請求\n";
        return true;
    }
}

function pay(PaymentMethod $m, int $amount): void {
    $m->charge($amount);
}

pay(new Card('4242-...'), 1500);
pay(new BankTransfer(), 1500);
```

- `Card` も `BankTransfer` も **同じ `charge()` で呼べる**
- 新しい支払い手段を増やしても `pay()` の中身は変えなくていい

---

## abstract と interface

| abstract class | interface |
|---|---|
| 共通実装も親に書ける | 契約 (宣言) だけ |
| 1 つだけ継承 | 複数 implement できる |
| 共通の枠 + 共通実装が欲しい | メソッドの型だけ揃えたい |

- 共通実装が欲しい → `abstract`
- 契約だけ → `interface` (次章 ch05)

---

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

✅ `abstract class` がインスタンス化できないことが分かる
✅ `abstract function` で子に実装を強制できる
✅ 「枠は親が決めて、具体は子で書く」設計を組める
✅ 抽象クラスとインターフェースの使い分けが言える

→ ドリルへ
