読者です 読者をやめる 読者になる 読者になる

ハウテレビジョン開発者ブログ

『外資就活ドットコム』を日夜開発している技術陣がプログラミングネタ・業務改善ネタ・よしなしごとについて記していきます。

なぜ DI が好きなのか PHP / Laravel でやってみた

プログラミング

夏はとにかく苦手、夏生まれの@KJ_BACCHUSです。 外出なんてしてたまるかと自宅で何かやろうと思いたった今日この頃。 Webエンジニアとしての知識が乏しいのでスキルアップを目的にアプリを作成しようと思います。 当社にはスマホアプリエンジニア(主にAndroid)としてjoinしたのですが、Webアプリも携わるようになってきたのでWebの勉強をしようと決意したという感じです。 いろいろ手広く業務出来るのはスタートアップならではの醍醐味。非常にやりがいを感じる日々です。

言語/framework 選定

さて、なにでアプリを作ろうかなと。 だれもが悩み、また楽しい行程の1つであろう言語の選定です。 とは言え私はPHPで決定です。 当社既存サービスがPHPを使用している且つ私はPHPを使ったことがないので、勉強という名目のため1択です。 楽しい行程即終了です。。。 しかし、Frameworkは好きなものを選ぼう!と色々調査することに。

Laravelをチョイス

馴染み易そう!という印象を受けたので決定しました。 慣れないWebアプリ/PHPをやるならまずは自分のやり易いコーディングができるのが一番と考えました。 ということで、どういった所に惹かれたのかを書いていこうと思います。

惹かれたワード

  • DI ← 影響力強し
  • ディレクトリ構成が自由
  • 開発スピード重視のフレームワーク

DIが好き

ほぼこれに尽きます。 まず当社Androidアプリ開発において、DI(依存性注入)しているという背景があります。 Android開発者なら、Dagger(当社アプリでも使用)を使用しているという方が多いのではないでしょうか。 Laravelなら、ちょっとそれに似た感じでコーディングできそうという期待が膨らみました。

なぜDIが好きなのか?

DIはjavaのフレームワークではよく使われるようになってきていると思います。 テストが書き易くなると言うのが大方の意見でしょう。DIで検索すると「テスト」という言葉がよく見受けられます。

それはまさにその通りなのですが、私が好きな理由としては疎結合なコードが書けるというところです。 まぁそれが「テストし易い」ということと直結するのですから意味は一緒かもしれませんが。。。

ただしDIコンテナを使用すれば必ずしも「疎結合」「テストし易い」コードになるわけではないので勘違いしてはいけません。 DIを駆使して開発していくには、抽象化と上手につき合っていく必要があります。

そういった意味でDIコンテナを使用して開発する事は、「疎結合」なコードを書く訓練になると感じています。

つまりDIを使用して「疎結合」なコードにしようという姿勢が好きなのです。

抽象化(interface)

「疎結合」なコードにするために「抽象化」が大切と述べましたので例を挙げてみます。

before

<?php

class HolidayOfManA {
    public function wakeUp() {
        return '8時起床';
    }

    public function breakfast() {
        return '朝食は食べない';
    }

    public function mainActivation() {
        return '家でゲームをする';
    }

    public function dinner() {
        return '夕食はパスタを食べる';
    }

    public function goToBed() {
        return '23時就寝';
    }
}

?>
<?php

class HolidayOfManB {
    public function wakeUp() {
        return '6時半起床';
    }

    public function breakfast() {
        return 'トーストを食べる';
    }

    public function mainActivation() {
        return '映画館に行く';
    }

    public function dinner() {
        return '夕食は親子丼を食べる';
    }

    public function goToBed() {
        return '24時就寝';
    }
}
?>
<?php
class ScheduleController extends AppController {

    public function view() {
        $holidayManA = new HolidayOfManA();
        echo $holidayManA->wakeUp();
        echo $holidayManA->breakfast();
        echo $holidayManA->mainActivation();
        echo $holidayManA->dinner();
        echo $holidayManA->goToBed();
    }
}
?>

上記の「HolidayOfManA」「HolidayOfManB」クラスはそれぞれ、男A、男Bの1日の行動を返すクラスです。 「ScheduleController」クラスのview()メソッドでは、男Aの1日の行動パターンを表示しています。

現状のview() メソッドだと、メソッド内で「HolidayOfManA」クラスのインスタンスを生成しているため密結合なコードと言えます。

これを「疎結合」にするために「HolidayOfManA」「HolidayOfManB」クラスから共通処理を抽出し、「抽象化」します。 そして、ここからLaravelのDIの機能を使用して記述していこうと思います。

after

<?php namespace MyProject\Humans;

interface HumanLifecycleInterface {
    public function wakeUp();

    public function eat($timezone);

    public function mainActivation();

    public function goToBed();
}
<?php namespace MyProject\Humans\Eloquent;

use MyProject\Humans\HumanLifecycleInterface;

class HolidayOfManA implements HumanLifecycleInterface {
    public function wakeUp() {
        return '8時起床';
    }

    public function eat($timezone) {
        if ($timezone == 'morning') {
            return '朝食は食べない';
        }
        else if ($timezone == 'night'){
            return '夕食はパスタを食べる';
        }
        return '';
    }

    public function mainActivation() {
        return '家でゲームをする';
    }

    public function goToBed() {
        return '23時就寝';
    }
}

?>
<?php namespace MyProject\Humans\Eloquent;

use MyProject\Humans\HumanLifecycleInterface;

class HolidayOfManB implements HumanLifecycleInterface {
    public function wakeUp() {
        return '6時半起床';
    }

    public function eat($timezone) {
        if ($timezone == 'morning') {
            return 'トーストを食べる';
        }
        else if ($timezone == 'night'){
            return '夕食は親子丼を食べる';
        }
        return '';
    }

    public function mainActivation() {
        return '映画館に行く';
    }

    public function goToBed() {
        return '24時就寝';
    }
}
<?php namespace MyProject\Controllers

use MyProject\Humans\HumanLifecycleInterface;

class ScheduleController extends AppController {

    protected $lifecycle;

    public function __construct(
        HumanLifecycleInterface $lifecycle) {

        $this->lifecycle = $lifecycle;
    }

    public function view() {
        echo $this->lifecycle->wakeUp();
        echo $this->lifecycle->eat('morning');
        echo $this->lifecycle->mainActivation();
        echo $this->lifecycle->eat('night');
        echo $this->lifecycle->goToBed();
    }
}

HumanLifecycleInterfaceが「抽象化」したinterfaceです。 共通の振る舞いを抽出したファイルになります。

「ScheduleController」のview()メソッドでインスタンスを生成することはせずに、コンストラクタでクラスを注入しています。 これがDI(依存性注入)です。

ある人の1日のスケジュールを表示する「ScheduleController」クラスは、誰のスケジュールかを知る必要がないという考え方です。 コンストラクタで注入するクラスが変更されることで、表示されるものが切り替わります。

変更しなければならないクラスが独立されるので、「疎結合」なコードとなりました。

では一体どのように実体を注入するのかというと...

app配下のconfigにあるapp.phpのprovidersに以下を記述します

<?php

return [
    'providers' => [

        'MyProject\Providers\HumansServiceProvider',

    ]
];

そして下記のHumansServiceProviderクラスにてHumanLifecycleInterfaceに注入する実体を記載します。 そのため、ここで実体を切り替えることができます。

この場合だとHolidayOfManAが使用されることになります。

<?php namespace MyProject\Providers;

use Illuminate\Support\ServiceProvider;

class HumansServiceProvider extends ServiceProvider {
    public function boot(){}

    public function register() {
        \App::bind('MyProject\Humans\HumanLifecycleInterface', 'MyProject\Humans\Eloquent\HolidayOfManA');
    }
}

※この例ではコンストラクタインジェクションですが、Laravel5にはメソッドインジェクションというものもあります。

いかがでしょうか。Android開発でDIを使用している方は、そっくり!!と感じたのではないでしょうか。 私はまさに、この方法にかなり惹き付けられました。

コード設計の概念

「抽象化」ができるようになると、プログラムの構成についても色々と思うところがでてきます。

各役割を明確化し、処理間の依存度を低くする。 そのためにインターフェースが存在する。

こう考える事で、本来のオブジェクト思考な開発が思い出されるのではないでしょうか。

図で表記すると以下のようなイメージです。

※APIアクセスする時の構成をイメージしています。

f:id:KJ_BACCHUS:20150807092238p:plain

フレームワークを使用していたことで、MVCで作成しなければいけないと囚われがちになっている考えが変わると思います。

もしも、「MVCで分割する」って結局どうすればいいの?とカオスになっているのであれば、「抽象化」を通して晴れやかになるかもしれません。

「抽象化」により依存度を低くした結果が「MVC」であって「MVC」で作成するのかどうかは開発者次第になるのではないかと。 つまり必ずしも「MVC」にしようと意識するのではなくて、「疎結合」なコードにしようと心がけるようになるのではないでしょうか。

おわりに

結局本記事ではLaravelのに機能ついてはあまり触れれませんでした。。。 ほとんどDIについてで終わってしまいましたので、今後Laravelの機能についてもっと書いていければと思います。

スマホエンジニアがWebアプリの設計を考えたらこうなったと捉えていただければ幸いです。