プログラマー歴20年の管理人が、これらの悩みを解決できるように、手を動かしながらOOPのインターフェースと抽象クラスを学べるように記事を書きました!
この記事を読むことで、インターフェースと抽象クラスの違いを明確に理解し、それぞれをどのように実装し、どのような場合に使えばよいのかがわかります。これにより、コードの設計と実装がスムーズに進みます。
この記事を読み終えた後、このコードを理解し、実践的なスキルが身についているはずです。
<?php
// インターフェースの定義
interface AnimalInterface {
public function makeSound();
}
// インターフェースの実装クラス
class Dog implements AnimalInterface {
public function makeSound() {
return "Woof!";
}
}
class Cat implements AnimalInterface {
public function makeSound() {
return "Meow!";
}
}
// ポリモーフィズムを示す関数
function describeAnimal(AnimalInterface $animal) {
echo $animal->makeSound() . "\n";
}
// インターフェースのテスト
$dog = new Dog();
$cat = new Cat();
describeAnimal($dog); // 出力: Woof!
describeAnimal($cat); // 出力: Meow!
// 抽象クラスの定義
abstract class Animal {
protected $name;
public function __construct($name) {
$this->name = $name;
}
abstract public function makeSound();
public function describe() {
return $this->name . " says " . $this->makeSound();
}
}
// 抽象クラスの実装クラス
class AbstractDog extends Animal {
public function makeSound() {
return "Woof!";
}
}
class AbstractCat extends Animal {
public function makeSound() {
return "Meow!";
}
}
// 抽象クラスのテスト
$abstractDog = new AbstractDog("Buddy");
$abstractCat = new AbstractCat("Whiskers");
echo $abstractDog->describe() . "\n"; // 出力: Buddy says Woof!
echo $abstractCat->describe() . "\n"; // 出力: Whiskers says Meow!
?>
これから、インターフェースと抽象クラスの基本概念、実装方法、そしてそれぞれの使い分けについて具体例を通じて解説します。
次のコードを書いてみましょう。interface
キーワードを使ってインターフェースを定義します。
AnimalInterface
という名前のインターフェースを作成し、makeSound
メソッドを宣言します。
<?php
// インターフェースの定義
interface AnimalInterface {
public function makeSound();
}
?>
インターフェースが定義できました。
インターフェースは、クラスがどんなメソッドを持つべきかを決めるためのテンプレートです。具体的に何をするかは書かれておらず、「このメソッドを持ちなさい」と決めるだけです。
コードの一貫性:
柔軟で拡張可能:
依存関係の低減:
多様な実装を許容:
続きのコードを書きましょう。インタフェースを実装していきます。
implements
キーワードを使って、クラスがインターフェースを実装していることを示します。makeSound
メソッドをそれぞれのクラスで具体的に実装します。
<?php
// インターフェースの定義
interface AnimalInterface {
public function makeSound();
}
// Dog クラスが AnimalInterface を実装
class Dog implements AnimalInterface {
public function makeSound() {
return "Woof!";
}
}
// Cat クラスが AnimalInterface を実装
class Cat implements AnimalInterface {
public function makeSound() {
return "Meow!";
}
}
?>
Dog
クラスと Cat
クラスは、それぞれ AnimalInterface
を実装しています。
describeAnimal
関数は AnimalInterface
型の引数を受け取ります。Dog
クラスと Cat
クラスのオブジェクトを作成し、describeAnimal
関数に渡します。
<?php
// インターフェースの定義
interface AnimalInterface {
public function makeSound();
}
// Dog クラスが AnimalInterface を実装
class Dog implements AnimalInterface {
public function makeSound() {
return "Woof!";
}
}
// Cat クラスが AnimalInterface を実装
class Cat implements AnimalInterface {
public function makeSound() {
return "Meow!";
}
}
// describeAnimal 関数
function describeAnimal(AnimalInterface $animal) {
echo $animal->makeSound() . "\n";
}
// オブジェクトの作成
$dog = new Dog();
$cat = new Cat();
// describeAnimal 関数を使って動作を確認
describeAnimal($dog); // 出力: Woof!
describeAnimal($cat); // 出力: Meow!
?>
$ php lesson3.php
Woof!
Meow!
インターフェースを使うと、異なるクラスのオブジェクトを同じ方法で扱えます。コードの柔軟性と再利用性が向上します。
継承を学習した際、使用したクラスを具体クラスします。具体クラスとインタフェースをなにが違うのでしょうか。
特徴 | インターフェース(Interface) | 具体クラス(Concrete Class) |
---|---|---|
定義内容 | メソッドのテンプレート(契約) | プロパティやメソッドの具体的な実装 |
実装 | 具体的な実装を持たない | すべてのメソッドが実装されている |
インスタンス化 | できない | できる |
使用目的 | クラスの契約を定義し、一貫性を持たせる | 直接オブジェクトを作成するため |
抽象メソッド | すべて抽象メソッド | 含まない |
継承関係 | クラスは複数のインターフェースを実装できる | 単一の親クラスからのみ継承できる |
アクセス修飾子 | すべてのメソッドはデフォルトで public | 任意のアクセス修飾子を使用できる |
例 | interface AnimalInterface {} | class Dog {} |
共通の機能の提供 | 持たない | 必要に応じて実装 |
設計の柔軟性 | 異なるクラス間で同じメソッドを保証する | 具体的な動作を実装 |
再利用性 | 異なるクラスに共通のメソッドを定義できる | 具体的な動作を他のクラスで再利用 |
使用例 | 異なるクラス間で同じメソッドを持つことを保証する | 共通のプロパティやメソッドを継承するため |
インターフェースは、クラスが実装すべきメソッドを定義し、異なるクラスに共通の決めるために使用します。
まず、抽象クラスを定義します。次のコードを書いてみましょう。
abstract
キーワードを使って抽象クラスを定義します。
Animal
抽象クラスには、コンストラクタと共通メソッド describe
を書きます。
makeSound
メソッドは抽象メソッドとして定義します。具体的な実装は持たないので書きません。
<?php
// 抽象クラスの定義
abstract class Animal {
public $name;
public function __construct($name) {
$this->name = $name;
}
// 抽象メソッドの定義
abstract public function makeSound();
// 共通メソッドの定義
public function describe() {
return $this->name . " says " . $this->makeSound();
}
}
?>
抽象クラスは、共通の機能や特性を持つクラスを作成するためのテンプレートです。具体的なクラスに共通する部分は抽象クラスで定義し、具体的な実装は子クラスで行います。
抽象クラスは abstract
キーワードを使って定義し、抽象メソッドを含むことができます。抽象メソッドは実装を持たず、サブクラスで必ず実装されるべきメソッドです。
次に、抽象クラスを継承する具体的なクラスを作成します。
Dog
クラスと Cat
クラスは、Animal
抽象クラスを継承します。
各クラスは、抽象メソッド makeSound
を具体的に実装します。
<?php
// 抽象クラスの定義
abstract class Animal {
public $name;
public function __construct($name) {
$this->name = $name;
}
// 抽象メソッドの定義
abstract public function makeSound();
// 共通メソッドの定義
public function describe() {
return $this->name . " says " . $this->makeSound();
}
}
// 抽象クラスを継承するクラス
class Dog extends Animal {
// 抽象メソッドの実装
public function makeSound() {
return "Woof!";
}
}
class Cat extends Animal {
// 抽象メソッドの実装
public function makeSound() {
return "Meow!";
}
}
?>
子クラスは抽象クラスを継承し、抽象メソッドを具体的に実装する必要があります。
最後に、抽象クラスと具体的なクラスを使ってオブジェクトを作成し、メソッドを呼び出して動作を確認します。
<?php
// 抽象クラスの定義
abstract class Animal {
public $name;
public function __construct($name) {
$this->name = $name;
}
// 抽象メソッドの定義
abstract public function makeSound();
// 共通メソッドの定義
public function describe() {
return $this->name . " says " . $this->makeSound();
}
}
// 抽象クラスを継承するクラス
class Dog extends Animal {
// 抽象メソッドの実装
public function makeSound() {
return "Woof!";
}
}
class Cat extends Animal {
// 抽象メソッドの実装
public function makeSound() {
return "Meow!";
}
}
// 抽象クラスのオブジェクトの作成と利用
$dog = new Dog("Buddy");
$cat = new Cat("Whiskers");
echo $dog->describe() . "\n"; // 出力: Buddy says Woof!
echo $cat->describe() . "\n"; // 出力: Whiskers says Meow!
?>
$ php lesson4.php
Buddy says Woof!
Whiskers says Meow!
子クラスに特定のメソッドの実装を必須にさせて具体的な実装内容は子クラスに任せたい場合に使えます。具体クラスでは親クラスで定義したメソッドを子クラスで使わなくても(オーバーライド)エラーになりません。
インターフェースとクラスを実際に動物と車のクラスを実装しながらイメージしていきたいと思います。
インタフェースを使用して動物と車に共通の「走る」というメソッドを設計します。
Runnable
という名前のインターフェースを作成します。このインターフェースは「走る」という動作を定義します。
interface Runnable {
public function run();
}
インターフェースは「このメソッドを持ちなさい」という約束(契約)を定義します。具体的な実装は含まず、クラスに対して「このメソッドを必ず実装しなさい」と指示するだけです。
次に、動物と車の抽象クラスを定義します。それぞれの抽象クラスは Runnable
インターフェースを実装し、共通のプロパティを持ちます。
抽象クラスは共通のプロパティやメソッドを持ち、部分的に実装されたクラスです。
インスタンス化(具体的なオブジェクトを作成すること)はできません。
子クラスはこの抽象クラスを継承し、抽象メソッドを実装しなければなりません。
abstract class Animal implements Runnable {
protected $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
abstract public function run();
}
abstract class Vehicle implements Runnable {
protected $model;
public function __construct($model) {
$this->model = $model;
}
public function getModel() {
return $this->model;
}
abstract public function run();
}
次に、動物と車の具体的なクラスを作成し、それぞれの「走る」動作を実装します。
具体クラスは抽象クラスを継承し、抽象メソッドを実装するクラスです。
具体的な動作を定義できます。インスタンス化してオブジェクトを作成することができます。
class Dog extends Animal {
public function run() {
echo $this->name . " is running on four legs.\n";
}
}
class Cheetah extends Animal {
public function run() {
echo $this->name . " is running very fast!\n";
}
}
class Car extends Vehicle {
public function run() {
echo $this->model . " is driving on the road.\n";
}
}
class Motorcycle extends Vehicle {
public function run() {
echo $this->model . " is speeding on two wheels.\n";
}
}
最後に、具体的なクラスを使ってオブジェクトを作成し、メソッドを呼び出します。
$dog = new Dog("Buddy");
$cheetah = new Cheetah("Chester");
$car = new Car("Toyota");
$motorcycle = new Motorcycle("Harley-Davidson");
$dog->run(); // 出力: Buddy is running on four legs.
$cheetah->run(); // 出力: Chester is running very fast!
$car->run(); // 出力: Toyota is driving on the road.
$motorcycle->run(); // 出力: Harley-Davidson is speeding on two wheels.
インターフェース
抽象クラス
具体クラス
異なるクラスが共通のメソッドを持ちつつ、それぞれの特徴に応じた具体的な動作を実装でき、コードの柔軟性と再利用性が向上します。