コラム

abstruct アブストラクトって呪文みたいじゃない?

※この記事の注意事項
・できるだけ初心者の方が理解しやすいように、細かい説明は省略しております。
・最後まで読み進められるように、多少ユーモアを交えて表現していることがあります。

========================================

abstruct アブストラクトという呪文を唱えるとどうなるのか

もちろん今回もPHPのクラスの話なのですが、abstructと付けることで、特別な親クラスになります。
これをクラスの抽象化と言います。

本当は抽象クラス継承クラスという言い方が正しいんだけど、色んなクラス名が出てきて混乱するので、ここではすべて親クラス、子クラスに統一してお話ししています。

抽象化したクラスは、普通に実装はできますが、インスタンス化することはできなくなります。newができないってこと。

つまり、自分自身だけでは何もできないクラスになってしまうのです。

その代わりに、子クラスに継承されることを前提としているので、子クラスに命令をすることができます。
どんな命令かというと、このメソッドは子クラスで定義してねと定義することができます。

/**
 * 自分ひとりではなにもできなくなった親クラス
 */
abstract class OyaClass
{
    //子クラスに実装を強制するメソッド
    abstract protected function setName($param);

    //子クラスに実装を強制するメソッド
    abstract protected function getName();

    //子クラスに実装を強制しない共通メソッド
    public function printOut() {
        echo $this->getName()."\n";
    }
}

ここではsetNameとgetNameを子クラスに強制しています。強制する場合は、親クラスでは実装はできません。

子クラスで継承して実装してみます。

class KodomoClass extends OyaClass
{
    private $name = '子ども';

    public function __construct() {
        # code...
    }

    public function setName($param) {
        $this->name = $param;
    }

    public function getName() {
        return $this->name;
    }
}

強制されたメソッド以外にも、メンバ変数やメソッド、コンストラクタなども普通に実装できます。ただ、強制されたメソッドを実装していないとエラーになります。

呼び出し方は以下のようになります。

//クラスのインスタンス化
$instance = new KodomoClass;

//親クラスの共通メソッドを呼び出し
$instance->printOut();

//子クラスのメソッドを呼び出し
$instance->setName('漢字にした子供');

//子クラスのメソッドを呼び出し
echo $instance->getName()."\n";

使い方はいたって普通ですよね。

ではabstructは何がしたいのか?というと、親クラスから子クラスに強制するってこと、但し実装は子クラスでやってくれということ。

それは2つ目の子クラスが出てきた場合も同じです。

abstract class OyaClass
{
    //子クラスに実装を強制するメソッド
    abstract protected function setName($param);

    //子クラスに実装を強制するメソッド
    abstract protected function getName();

    //子クラスに実装を強制しない共通メソッド
    public function printOut() {
        echo $this->getName()."\n";
    }
}

class KodomoClass1 extends OyaClass
{
    private $name = '子ども';

    public function __construct() {
        # code...
    }

    public function setName($param) {
        $this->name = $param;
    }

    public function getName() {
        return $this->name;
    }
}

class KodomoClass2 extends OyaClass
{
    private $value = 1;

    public function setName($param) {
        $this->value += $param;
    }

    public function getName() {
        return $this->value;
    }
}

//子クラス1
$instance1 = new KodomoClass1;
$instance1->setName('漢字にした子供');
echo $instance1->getName()."\n";

//子クラス2
$instance2 = new KodomoClass2;
$instance2->setName(100);
echo $instance2->getName()."\n";

ここでは親クラスで型指定をしていないので、子クラス1では文字列の名前として実装し、子クラス2では数値の足し算として実装しています。
こういう実装は混乱の原因になるので型指定も行った方がよいですね。
(型指定はPHP7.0から可能になりました。この記事では記載しません。)

abstruct アブストラクト使用の注意点

注意点なんですが、親クラスで強制したメソッドの名前はもちろんのこと、引数の数や名前なども同一でなくてはなりません。これらをまとめてシグネチャといいます。シグネチャも同一にしてくださいと表現します。

親クラスのsetNameを見ると、アクセス権がprotectedになっています。
子クラスのsetNameを見ると、アクセス権がpublicになっています。
ここも本来の考え方は同一であるべきなのですが、親よりゆるいアクセス権は認められているのです。ですので、子クラスではprotectedとpublicのどちらかで設定することが可能なのです。

親クラスから子クラスへの継承へは強制力があるのですが、孫クラスへは強制力がないんですよね。
まるで現実世界のようですね。

以下は孫クラスではabstractメソッドを実装していませんがエラーにはなりません。

abstract class OyaClass
{
    //子クラスに実装を強制するメソッド
    abstract protected function echoName($param);
}

class KodomoClass extends OyaClass
{
    public function echoName($param) {
        echo $param."\n";
    }
}

class MagoClass extends KodomoClass
{
    public function printOut() {
        echo "孫クラス";
    }
}

$instance1 = new KodomoClass;
$instance1->echoName("子クラス");

$instance2 = new MagoClass;
$instance2->printOut();
まとめですが、

abstructが付いた親クラスは抽象クラスとなり、子クラスで実装して欲しいメソッドにはabstructを付け、実装は子クラスに任せる!
abstructが付かないメソッドも実装して、共有することができる。

抽象クラスとなった親クラスを継承した子クラスでは、親クラスでabstructが付いているものはすべて実装する必要があり、かつ型やヒント、名前などのシグネチャも同一でなければならない。
子クラスでは、abstructが付いたもの以外でも、自由にメソッドを追加したり実装することができる。

割と優しい親ですよね。僕もそんな親になろうと思いました。
そして孫には甘々です。きっと僕もそうなります。

========================================

▼PHPのオブジェクト指向とデザインパターン記事一覧

関連記事

コメント

  • トラックバックは利用できません。

  • コメント (0)

  1. この記事へのコメントはありません。