Menu
Coddy logo textTech

インターフェースと抽象クラスの比較

CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 30/91。

インターフェースと抽象クラスの両方を学んだので、それぞれをいつ使うべきか明確にしましょう。似ているように見えるかもしれませんが、目的は異なり、できることにも違いがあります。

機能 インターフェース 抽象クラス
メソッド method のシグネチャのみ abstract および具体的なメソッド
プロパティ 定数のみ property を持てる
多重継承 class は複数を Implement できる class は1つだけ extends できる
Constructor __construct のシグネチャを declare できる(body なし) body を持つ Constructor を定義できる

互いに関係のないクラスが実装できる契約を定義したい場合は、interfaceを使用します。たとえば、PrinterLoggerは共通の祖先を持たなくても、どちらもWritable interfaceを実装できます。

共通のコードを共有する関連クラスがある場合は、abstract classを使用します。DogクラスとCatクラスの両方で同じeat()メソッドの実装が必要な場合、abstractなAnimalクラスを使えば、そのコードを一度だけ記述できます。

両方のアプローチを組み合わせることもできます。クラスは抽象クラスを拡張しながら、複数のインターフェースを実装できます。

<?php
abstract class Animal {
    protected $name;
    
    public function eat() {
        return $this->name . " is eating";
    }
}

interface Swimmable {
    public function swim();
}

class Duck extends Animal implements Swimmable {
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function swim() {
        return $this->name . " is swimming";
    }
}

重要なポイント: 関係のない class 全体で能力を定義するには interface を選びます。関連する class 間でコードを共有する必要がある場合は、abstract class を選びます。

challenge icon

チャレンジ

簡単

abstract class と interface のどちらを使うべきか、また両方のアプローチを効果的に組み合わせる方法を示す vehicle system を構築しましょう。

継承を通じて vehicles が共通の properties を共有し、さらに interfaces を通じて追加の capabilities を獲得できる system を作成します。これは現実世界の設計上の判断を反映しています。car は "is a" vehicle(継承)ですが、同時に refuelable である "has the capability"(interface)も備えています。

コードを4つのファイルに分けて整理します。

  • Refuelable.php:単一の method signature refuel($amount) を持つ Refuelable interface を Define します。この capability は、継承階層に関係なく、refueling が必要なあらゆる class に適用できます。
  • Vehicle.php:すべての vehicles に共通する functionality を提供する abstract Vehicle class を Define します。protected $brand property と、0 から始まる protected $fuelLevel property を持たせます。constructor は brand name を accepts します。brand を返す concrete getBrand() method、current fuel level を返す concrete getFuelLevel() method、そして child classes が Implement する必要のある abstract drive() method を含めます。
  • Car.phpVehicle を extends し、Refuelable を implements する Car class を Create します。先頭で required files の両方を Include します。constructor は brand を accepts し、それを parent constructor に passes します。drive() を Implement して "[brand] car is driving" を返します。refuel($amount) を Implement して、amount を fuel level に Add し、"[brand] refueled to [fuelLevel] liters" を返します。
  • main.php:Car file を Include します。2つの inputs、つまり brand name と fuel amount(integer に convert)を受け取ります。brand を使って Car を Create します。最初の line に drive() の結果を Print し、次に2行目に fuel amount を使った refuel() の結果を Print します。

この challenge は重要な違いを示しています。abstract Vehicle class は code を共有し、"is-a" relationship を確立します。一方、Refuelable interface は、generator や boat のような、vehicle とは無関係な他の classes にも、それらが vehicles である必要なく適用できる capability を追加します。

自分で試してみよう

<?php
require_once 'Car.php';

// 入力を読み取る
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));

// TODO: ブランドでCarを作成する

// TODO: 1行目にdrive()の結果を出力する

// TODO: 2行目に燃料量でrefuel()の結果を出力する
?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: PHPオンラインコンパイラ