新倉氏の自己紹介

新倉涼太氏(以下、新倉):さっそく「開発のハードルを下げるInterface」という題名で話していきたいと思います。

(スライドを示して)これは私ですね。PHP歴は12年ぐらいで、最近はLaravelを使っています。そんなにLaravelにこだわりがあるわけじゃなくて、DIがあったりテストが書きやすかったりしていいなと思っているぐらいですね。最近はM&Aクラウドにジョインしています。

PHPにおける「Interface」と「Implements」

ではさっそく「PHPにおけるInterfaceとは何なのか」を説明していきます。Interfaceはみなさん知っているとおりですね。クラスに必要なメソッドの宣言だけをまとめたものになります。こいつ自身は実装にはなっていませんが、こいつの対になるものとして、Implementsというものがあります。

こいつはInterfaceに対する実装、要するにInterfaceに宣言されているメソッド全部を実装しなければならないものになります。

Interfaceは型として使えます。(スライドを示して)これはfunction helloの引数の型宣言にInterfaceを使っているところですが、Interfaceを型として使うと、Interfaceを実装した、Implementsしたものを全部引数として使えることができるという特徴を持っています。ここまでがInterfaceの基本です。

Interfaceを使うことで、実装の入れ替えが簡単にできるようになる

なぜInterfaceを使うのかの説明ですが、そのためにはまず依存という考え方を説明していきたいと思います。あるモジュール、OneModuleが外部モジュールのOuterModuleというものを必要とする時、「OneModuleはOuterModuleに依存している」と言います。

(スライドを示して)例えば、下のようなコードで表されていますね。プロパティとしてOuterModuleを持ってきて、生成時にOuterModuleを入れてもらわないと動かないものがあります。これが依存ですね。

書き方はこういうふうに書きますという話。

依存するモジュールが実装である場合、OneModuleがOuterModuleに依存している状態の場合、OuterModuleとOneModuleは要するに一体になって初めて完結するわけですね。

OuterModuleが振る舞いを変えると、当然OneModuleは自分の振る舞いを変えていくような状況になります。

ところで、先ほどInterfaceの型宣言の話をしました。ということで、こちらも当然型宣言をしてInterfaceを入れることができます。そうするとモジュールは先ほどの実装ではなくInterfaceに依存するような状況を作ることができます。これ自体は別に大したことはないんですね。

「Interfaceで依存しているのと実装に依存しているのと何が違うの?」という話になるんですが、これをするとちょっとおもしろい事態が発生します。

それが依存性の逆転というものです。

例えばSomeModuleが、あるInterface、SomeInterfaceに依存していると考えます。そうすると、実際にはInterfaceを入れるよりはInterfaceを実装したImplementsを入れる必要が出てくるんですが、このImplementsは結局のところSomeInterfaceの実装になっているということなので、両方から矢印を指すことができる。これが依存性の逆転という現象です。

これが起こると何がうれしいのかという話ですが、WhatとHowの分離ができるのがうれしいところです。結局のところ、SomeInterfaceはSomeModuleが必要、「こいつがほしいんだ」ということをまとめたものになるわけです。要するにSomeInterfaceのSomeModuleによる要求仕様だと。

言い換えれば「Whatである」と考えることができるわけですね。じゃあImplementsは何なのかというと簡単な話で、要求仕様に対して実装したもの。もしくはWhatに対するHowになるわけです。

これで重要なのは、モジュールというものは、あくまで要求仕様にのみ依存している状況になっているので、その向こうにある実装に関しては、特に興味を持っていないという状況が作れるわけですね。

そうすると、結局のところは先ほどのInterfaceを型として使った時の特徴と一緒で、SomeModuleはInterfaceを満たしたImplementsであれば何でも受け入れることができるという状況を作ることができるわけですね。実装の入れ替えが簡単にできるようになるというのが、一番の利点になっています。

(スライドを示して)「実装ってそんなにいっぱいあるの?」という感じですが、ここに実装の例を示しました。データを文字列で検索したい時を考えると、例えばMySQLの特定カラムのLike検索とか、全文検索があるとか。「全文検索をするなら『ElasticSearch』やろ」とか、そんな感じでいろいろな実装のパターンを考えることができる。

ということで、Whatに対していろいろなHowを考えることができるということで、その中から適切なものを選ぶことができるのがInterfaceを使った時の利点になります。

Interfaceで開発のハードルを下げる

このInterfaceや依存性の話を使って開発のハードルを下げるということを今から話していきたいと思います。開発のハードルが何なのかを説明しますが、まずはプロダクトの企画の段階を考えてみましょう。企画の方が「〇〇をやりたい」と思った時に、エンジニアの人が「工数を見積もってみようかな」という話をします。

そうすると、品質と速度のトレードオフが発生するわけです。よりやりたいことを確実に実現する。つまり、品質を上げるためには品質をどんどん上げていく。そのためには開発速度を下げないといけない。要するに、リリースまでにかかる時間が長くなってしまうわけですね。その逆もしかりで、リリースを早くしようとすると品質が疎かになっていく状況が発生します。

企画側は早くリリースしたいと思っている一方で、エンジニアとしては全体の実装を見た時に「いやいや、そんな簡単にいきませんよ」「こんなにかかりますよ」というところまでかなり時間をかけるという見積もりをしてしまう。そうすると「それじゃあ投資対効果が合わないじゃん」というところで、なかなか企画側とエンジニア側の方針が合わなくて、開発を始めることが困難になってしまう。「じゃあやるのは止めようかな」みたいな話が発生しちゃったりするわけですね。

これが要するに開発のハードルが上がっている状態。開発を始めようとしても開発に移れない状態です。

エンジニアとしても別に意地悪で「クオリティを上げよう」と言っているわけじゃなくて、低クオリティでリリースしたとしても「あとでどうせハイクオリティにするんでしょう」と。低クオリティの状態のものをクオリティが高い状態に持っていくためには、実装の修正とかがすごくいっぱい必要なわけですね。

その最中はデプロイできないし、開発ブランチとメインブランチの差分が広がっちゃったりしていろいろ大変というのがあるわけですね。

ただ、ここまでの話は要するにエンジニアが全体の実装をマルッと見積もっちゃった場合に発生する話であって、(そんな時に)いったんInterfaceのことを考えるわけです。

先ほど「Interfaceは要求仕様だよ」という話をしました。企画側がやりたいこと、ほしいものに関して、Interfaceというかたちでいったん切ってみようと。こいつを置くところをまずは第1の目標にする。

Interfaceを作ると、そいつを使うために、UIからアプリケーションを通してアプリケーションをうにゅうにゅやって(から)Interfaceを使うという経路が考えられるわけなんですが、Interfaceを使うためには絶対この経路が必要だとわかるので、最低限の実装はここで発生する。これをベースラインと見ることができるわけですね。

そうすると「このベースラインはとにかく絶対にやらないといけない」と企画側と合意を取ってから、その先の実装についてあらためて考えることができるわけです。例えば、ハイクオリティな実装、ロークオリティな実装、もしくはただのモックみたいな実装という、いろいろなパターンを選択肢として提示できるようになるわけです。

戦略としてはいろいろ取るわけですね。ロークオリティな実装を先にやってユーザーの使用率を見てみたり、端からハイクオリティのやつを作ってユーザーの満足度を上げたり。いろいろな戦略が取れるわけで、そこで落としどころを見つけましょうというところ。

さらに、先ほど言っていた入れ替え、交換可能性を使って「こんなこともできるよ」というものですが、ロークオリティの状態で実装してから、あらためてハイクオリティなものを作ろうとなった時に、ロークオリティなものはそのままにしておいて、ハイクオリティのものだけ別実装として作っちゃう。

それでどんどん実装していって、完成したら切り替えちゃう。これだけでロークオリティからハイクオリティへのバージョンアップというものが完了するということが実現できるわけですね。

実際にこれをどうやってやるかというと、最近だとDI(Dependency Injection)とかがだいたいのフレームワークに入っているかと思うんですが、DIとかを使って実現するわけですね。

例えば、SomeInterfaceに対してLowQualityImplementsが実装されていたと。そいつに対して1行、「HighQualityに変えてね」と変えるだけですね。これだけで全体が一気に変わるわけです。

お客さん、要するにユーザー側からしてみればWeb上で起こっていることなので、インストールとか何もせずに、我々がただコードをデプロイするだけですべての入れ替えが一瞬にして行われるというのがわかるわけです。

こんな感じで、Interfaceを介して複数の実装の選択肢を用意するというところと、低品質でもあとから高品質にすることは「そんなに難しくないよ」というのを用意して、あとから開発できるようにするというのを用意してあげる。

こういうふうに、実装とか戦略の選択肢を柔軟に用意する。「じゃあ、このあたりを落としどころにして開発を開始しましょう」という状態を作ることができるということですね。これが開発のハードルを下げるという話になるわけです。

実装の選択肢を見やすくして、開発のハードルを下げましょう

まとめです。PHPではInterfaceという機能があります。これはメソッドの宣言のみを切り出したものです。モジュールをInterfaceに依存させると、実装の入れ替えは簡単にできます。Interfaceを介して実装の選択肢を見やすくして開発のハードルを下げましょうということで、今回の話を終わりにしたいと思います。ありがとうございます。