採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

抽象クラス (abstract)

テクノロジ系 / class-advanced (継承 / オーバーライド / abstract / interface / static / const / final)

抽象クラス (abstract)

Lesson 7 / Chapter 4

ゴール

abstract class PaymentMethod {
    abstract public function charge(int $amount): bool;
}
  • abstract classそのままは new できない ことが分かる
  • 子クラスに charge() の実装を強制できる
  • 「枠は親、具体は子」という設計が書ける

抽象クラスとは

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

書き方

<?php
abstract class PaymentMethod {
    abstract public function charge(int $amount): bool;
}
  • abstract class でクラス自体を抽象に
  • abstract function で中身を書かないメソッド宣言
  • 本体 { ... } を持たず、 ; で終わる

new できない

<?php
$p = new PaymentMethod();
// ❌ Fatal error: Cannot instantiate abstract class PaymentMethod
  • 抽象クラスは直接 new できない
  • 「決済できる支払い手段」は具体形 (Card / BankTransfer) があってこそ意味を持つ
  • 必ず子クラスを作って使う

子クラスで実装する

<?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
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);
  • CardBankTransfer同じ charge() で呼べる
  • 新しい支払い手段を増やしても pay() の中身は変えなくていい

abstract と interface

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

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

abstract class がインスタンス化できないことが分かる

abstract function で子に実装を強制できる

✅ 「枠は親が決めて、具体は子で書く」設計を組める

✅ 抽象クラスとインターフェースの使い分けが言える

→ ドリルへ

考えてみよう

既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。

ドリル 01 — abstract PaymentMethod を継承した Card

題材

EC サイトの決済処理。複数の支払い手段を共通の枠で扱うため、親で charge() の存在だけ強制し、具体の処理は子に書かせる。

> 本番では Stripe / PAY.JP の SDK を呼ぶが、ここでは練習のため文字列で返す。

やること

abstract class PaymentMethod を定義してください。

  • abstract public function charge(int $amount): string; を宣言する (中身は書かない)

PaymentMethod を継承した Card クラスを定義してください。

  • charge(int $amount): string を実装して "カードで {amount} 円を決済しました" を返す

Card をインスタンス化し、charge(1500) の結果を 1 行出力してください。

期待される出力

カードで 1500 円を決済しました

ヒント

  • abstract class PaymentMethod { abstract public function charge(int $amount): string; }
  • class Card extends PaymentMethod { ... }
  • echo $c->charge(1500) . "\n";
  • 文字列連結は "カードで {$amount} 円を決済しました" でも . ' '. でも OK

採点

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