クラスを水平方向に拡張できるPHPの「トレイト」Web業界で働くためのPHP入門(19)(1/3 ページ)

オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載。今回は、クラスに対して横断的に機能を追加できる「トレイト」について解説します。

» 2018年06月27日 05時00分 公開

 オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載「Web業界で働くためのPHP入門」。

 今回は、クラスに対して横断的に機能を追加できる「トレイト」について解説します。

「トレイト」とは

 トレイトは、継承とはまた別の方法でクラスを拡張できる仕組みです。最初に少し概論的な話をしておきましょう。

垂直方向にクラスを拡張する継承の問題点

 連載第17回でクラスを拡張できる仕組みとして継承を解説しました。その継承は親クラスを文字通り引き継ぎ、そこに子クラス独自のプロパティやメソッドを追加することで拡張します。例えば、スポーツ選手を表すSportsPlayerクラスがあったとします(リスト1)。

<?php
class SportsPlayer
{
    //名前のプロパティ。
    private $name;
    //コンストラクタ。名前プロパティに値をセットする。
    public function __construct(string $name)
    {
        $this->name = $name;
    }
    //名前プロパティのゲッタ。
    public function getName():string
    {
        return $this->name;
    }
}
リスト1 phplesson/chap19/SportsPlayer.php

 これを継承したTennisPlayerクラス、HandballPlayerクラス、BaseballPlayerクラスを考えます。それぞれクラス名の通り、「テニス選手」「ハンドボール選手」「野球選手」を表します。それぞれのクラスには、その球技で使う道具、例えばテニスならラケット、野球ならバットとグローブを表すプロパティ(ハンドボールは道具を使いません)と、その球技独特の何かメソッドを追加するとします。

 ここで、メソッドとして、その球技の主要な動作を考えます。例えば、テニスならば「打つ」で、ハンドボールならば「投げる」です。野球にはこの両方の動作が含まれます(図1)。

図1 SportsPlayerを継承して処理を追加

 ここで、もう一段階考えます。テニスと野球には同じ動作(=処理)である「打つ」が含まれています。共通の処理がある場合は、それは親クラスに記述した方が継承のメリットを生かせます。そのように考えてSportsPlayerクラスに「打つ」を追加したとします(図2)。

図2 親クラスであるSportsPlayerに「打つ」を追加

 すると、問題が生じます。HandballPlayerクラスにも「打つ」が含まれてしまうことになります。ハンドボールに「打つ」は不要です。同様に、「投げる」を親クラスに追加したら、今度はTennisPlayerクラスに不要な処理が含まれてしまいます。親から子へ、子から孫へと内容を引き継ぐ継承は、縦方向(垂直方向)のクラスの拡張といえますが、垂直方向故に、このような問題が生じます。

クラス横断的に機能を追加できるトレイト

 この問題を解決するのが、トレイトです。トレイトは水平方向のクラスの拡張といえます。それは、各クラスにプロパティとメソッドのカタマリを、あたかも機能拡張のように追加できます。例えば、「打つ」というトレイトと「投げる」というトレイトの2個を用意しておき、TennisPlayerクラスには「打つ」トレイトを組み込みます。HandballPlayerクラスには「投げる」トレイトを組み込みます。そして、BaseballPlayerクラスには両方のトレイトを組み込みます(図3)。

図3 クラス横断的に機能追加できるトレイト

トレイトの作成は「trait」宣言

 では、実際にソースコードでみていきましょう。まず、「打つ」を表すトレイトであるHitTraitトレイトを作成します。これは、リスト2のようになります。

<?php
trait HitTrait  // (1)
{
    public function hit():void  // (2)
    {
        print(__CLASS__."がボールを打ちます。<br>");  // (3)
    }
}
リスト2 phplesson/chap19/HitTrait.php

 トレイトを作成するには、(1)のように「trait」宣言を使います。トレイト名は、HitTraitのように「○○Trait」とするのが習わしです。

構文「トレイト」

trait ○○Trait

{

  プロパティ

  メソッド

   :

}


 トレイトはクラス機能の一部を切り出したものですので、クラス同様にプロパティやメソッドを記述できます。リスト2では「打つ」を表すhit()メソッドを記述しています。それが(2)です。プロパティやメソッドの記述方法はクラスと同じです。

 メソッド内の処理は「打つ」を模倣したようなものにするために単に「ボールを打ちます」と表示するようにしました。それが(3)です。ただ、そのときに「どのクラスで、このトレイトが利用されたのか」を明確にするように、主語にクラス名を表示させるようにしています。PHPでは実行中のクラス名を表示するための定数が用意されており、(3)の「__CLASS__」が該当します。

 さて、同じように「投げる」を表すThrowTraitトレイトを作成しましょう(リスト3)。

<?php
trait ThrowTrait
{
    public function throw():void
    {
        print(__CLASS__."がボールを投げます。<br>");
    }
}
リスト3 phplesson/chap19/ThrowTrait.php

 基本構造はHitTraitトレイトと同じです。

       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。