はじめに
本記事はコネヒト Advent Calendar 2017の14日目のエントリーになります。
こんにちは!コネヒト株式会社でサーバーサイドエンジニアをやっている @itosho です。
僕は以前、会社のテックブログでもしDHHさんがCakePHPのコントローラーを書いたらという記事を書いたことがあり、普段からRubyから学んだ技術や考え方をPHPにも応用する癖があるのですが、今日はそんな妄想シリーズの第2弾をお届けしたいと思います。
Sandi Metzルールとは?
いきなりですが、皆さんはSandi Metzルールというのをご存知でしょうか?
Rubyistの方はご存知の方が多いかもしれませんが、このルールは『オブジェクト指向設計実践ガイド~Rubyでわかる進化しつづける柔軟なアプリケーションの育て方』の著者として有名なSandi Metzさんが唱えたRubyで綺麗な設計を身に付けるためのルールになります。
具体的には以下の4つが守るべきルールとして挙げられています。
- クラス内のコードは100行以下にする
- メソッド内のコードは5行以下にする
- メソッドの引数は4つ以下にする
- コントローラーでインスタンス変数に出来るオブジェクトは1つだけにする
パッとみただけでも、なんだかよさそうな感じがしませんか?
というわけで、今日はこのSandi MetzルールのPHP版を考えてみたいと思います。
各ルール考察
ルール1. クラス内のコードは100行以下
まず、このルールのメリットは単一責任の原則を徹底出来るようになることですね。Rubyと同様に(現在は)オブジェクト指向言語であるPHPでもこのルールは充分適用出来そうです。
ただ、この100という数字はPHPでも適切でしょうか?
RubyとPHPではシンタックスや表現力が異なるので、もしかしたら、PHPの場合はもっと閾値を長く(もしくは短く)したほうがいいかもしれません。
というわけで、「推測するな計測せよ」の精神のもと、Ruby / Rails製の代表的なオープンソフトウェアであるRedmineとそのクローンであるPHP / CakePHP製のCandyCaneのコントローラーファイルを比較してみました。1
項目 | Redmine | CandyCane |
---|---|---|
ファイル数 | 50 | 35 |
総行数 | 7421 | 8152 |
平均行数 | 148 | 232 |
計測方法: 2
という結果になり、PHPがRubyに比べて1.57倍長くなっていることが判明しました。
というわけで、PHPでは100行ではなく150行を閾値にしてみたいと思います。
ルール2. メソッド内のコードは5行以下
ファットクラス同様、ファットなメソッドは可読性が落ちたり、変数のスコープが長くなったりするので、メソッドを短くするメリットは大きいと思います。
また、細かい処理をプライベートメソッドにすることで、その処理に名前を付けることもメリットになりそうです。
では、こちらも5という閾値がPHPでは適切なのかどうかをRedmineとCandyCaneを用いて、比較してみたいと思います。
項目 | Redmine | CandyCane |
---|---|---|
メソッド数 | 17 | 20 |
総行数 | 492 | 737 |
平均行数 | 29 | 37 |
計測方法: 3
という結果になり、PHPがRubyに比べて1.27倍長くなっていました。
というわけで、こちらのルールは5行ではなく6行を閾値にしてみたいと思います。
ルール3. メソッドの引数は4つ以下
このルールはルール2.が徹底出来ていれば、ある程度自然に守ることが出来るかもしれませんが、引数が多いメソッドは用途がはっきりしないことが多いです。
また、引数がたくさんあるのに、実は呼び出し時に指定不要な引数があることも多く、そういうメソッドを使うと、往々にして密結合になるので、このルールを守るメリットはPHPでも当然享受出来ます。
併せて、ハッシュ(連想配列)によるオプションも引数としてカウントするとルールには明記されているので、大量のオプションを設定出来る配列の引数も撲滅することが出来そうです。
尚、4という閾値については『インタフェースデザインの心理学――ウェブやアプリに新たな視点をもたらす100の指針』でも"人はどう記憶するのか"という章で、
一度に覚えられるのは4つだけ
という記述があるので、こちらのルールはそのまま採用してみたいと思います。
ルール4. コントローラーでインスタンス変数は1つ
こちらのルールはRuby / Rails特有の考えなので、そのままPHPに応用することは出来ませんが、基本的にはコントローラーとビューのやりとりをシンプルにすることが目的です。
CakePHPで言うと、コントローラーからビューにセットする変数を1つになるようにします。
もちろん、例えば、ダッシュボードのように様々な情報(オブジェクト)を表示する必要がある画面の場合、単純なやり方では上記のルールを守ることが難しいです。
その場合は、Facadeパターンを使って、様々な情報を内包したダッシュボードオブジェクトをビューに渡すことが、このルールを守るためのコツのようです。
実際のコードを参照していただいたほうがイメージしやすいかもしれません。
また、元記事のThoughtWorks(マーティン・ファウラーが在籍していることで有名な会社)では、ビューに渡さないメモ化用のインスタンス変数は例外として _
というprefixを付けるという運用をしているそうです。
というわけで、こちらのルールもPHPでも充分実現出来るので、そのまま採用してみたいと思います。
PHP版Sandi Metzルール
まとめるとこんな感じです!
- クラス内のコードは150行以下にする
- メソッド内のコードは6行以下にする
- メソッドの引数は4つ以下にする
- ハッシュによるオプションも引数としてカウントする
- コントローラーでインスタンス変数に出来るオブジェクトは1つだけにする
- コントローラーからビューに渡す変数を1つだけにする
ルール0
実はSandi Metzルールにはもうひとつルールがあって、ルール0として、
- 適切な理由がある場合
- コードレビュアーやペアプロのパートナーが許可した場合
は、上記4つのルールを破ってもよいと宣言されています。
特にルール3.はRailsやCakePHPのヘルパーメソッドを使う場合、そもそも難しいことが多いですし、ルール2.も仕様によってはどう頑張っても守ることが難しい場合も多々あると思うので、個人的には落とし所というかバランスが素晴らしいルールだなと感じています。
おわりに
まとめ
各ルールの閾値が適切なのかどうかという議論はもちろんとあると思いますし、そもそも大リーグボール養成ギブス的なルールでもあるので、Strictに守っていくのは実際の開発現場では難しいかもしれません。
しかし、このルールをちょっと意識するだけでも、コードの出来は全然違うと思うので、PHPでもこのルールを取り入れ、少しでも綺麗なコードを書くことで、今後もユーザーに価値のあるサービスを届けていきたいと思います!
ちなみに、Sandi Metzルールを布教していたら、サーバーサイドエンジニア仲間がSlackでこんなリマインダーを仕掛けてくれていました
明日の予告
明日(15日目)のコネヒト Advent Calendar 2017はコネヒトが誇るAndroidエンジニア @tommykw のターンです!お楽しみに!
参考文献 / サイト
- 『オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方』(技術評論社, 2016)
- 著:Sandi Metz / 訳:髙山 泰基
- 『インタフェースデザインの心理学 ―ウェブやアプリに新たな視点をもたらす100の指針』(オライリージャパン, 2012)
- 著:Susan Weinschenk / 訳:武舎 広幸, 武舎 るみ, 阿部 和也
- Sandi Metz' Rules For Developers
- 綺麗な設計を身に付けるためのSandi Metzルール
-
RedmineとCandyCaneではフレームワークのバージョンやメンテナンス状況が異なるため、厳密な比較は出来ないかもしれませんが、似ているソフトウェアということで選ばさせていただきました。 ↩
-
行(ステップ)数はコメントを除いたコードの行数になります。それぞれのコントローラーディレクトリ内のファイルを対象にしていますが、CandyCaneのComponentディレクトリは対象外にしております。 ↩
-
どちらのリポジトリでも最も行数が長いコントローラークラスである
IssuesController
クラス内のメソッドを対象にしています。本当は全コントローラークラスのメソッドを対象にしたかったけど、簡単に計測出来るツールをみつけられなかったので…。 ↩