PHPはHypertext PreProcessorの略語ということになっていますが、実はPersonal HomePageの略だったという超ダサい出自を持っています。ただこの出自は伊達ではなく、ホームページ作っているときに便利だなーという機能を集めていったらPHPになったという、「必要は発明の母」を地でいく言語です。
その由来から察するに、「PHP書けるよー」という人の多くは情報処理の高等教育を受けて来たというより、なんらかの事情でホームページを作っていて、それからPHPを書くようになったというケースが多いんではないでしょうか。
メールフォーム作ったとか、WordPress導入したらPHP書くことになったとか、MySQLと組み合せてみたくなったとか、そういうケースです。僕もそんな感じですね。
僕をはじめとして、こうした人の書くPHPコードは、プログラミングの体系的な知識やWebサイト構築の経験が不足した状態ですので、変なコードになります。いわゆる、スパゲッティコードという奴でしょうか。
はじめからできる人はいないので、それはそれで構わないのですが、いつまでも自分の書いた糞コードをメンテし続けるのは嫌ですよね。
そうなると当然リファクタリングが必要になるのですが、ある程度膨大な量のコードを書いてしまった後で、さあどうしよう、となるわけです。よくあるのは、特定の処理を任せていたクラスがめちゃくちゃ膨大になってしまって見るのも嫌というパターンですね。
なぜ膨大なクラスが生まれるのか
「オブジェクト指向については理解しています」とか言っちゃうと道端で絡まれる可能性があるので、まあなんとなく「カプセル化とポリモーフィズムと、えっと……」とか答えておけばいいとは思うのですが、オブジェクト指向プログラミングには一連の処理をクラスに分けて処理を分担させていくという作業が含まれます。この作業がけっこう難しいです。
一連の処理は単なるタスクの山ですが、それをオブジェクト指向によって実現するとなると、いきなり名詞と動詞を沢山考えることになります。ここが躓きのもとです。この結果、一回しか使わないようなメソッドを幾つも持ったグローバルオブジェクトが各ページで何度も呼び出されるような事態となります。
<?php // ↓こんなのがページのあちこちに出てくる $my_site_creator->list->render('page1'); ?>
オブジェクト指向の説明では、抽象的な概念としてのクラスについて書かれていますが、具体的なモノを知らずしていきなり抽象を発見するのは至難の業です。
デザインパターン? MVC?
デザインパターンは抽象的すぎてわからん
さて、オブジェクト指向プログラミングの世界にはデザインパターンという概念があり、いわば型のようなものなんですが、わりと抽象的な概念のため、経験のない人には理解しづらいんですね。自分が作ろうとしている処理がどのパターンにあてはまるのかは、ぱっとわかりません。Do You PHP?でPHPデザパタが無料で公開されているので、試しに見てみてください。
僕の大学時代の同級生が家庭教師の中一の教え子にいきなり英語の五文型を教えようとしてクビになったことがふと思い出されます。
価格¥5,280
順位25,655位
著Erich Gamma
発行SBクリエイティブ
発売日1999 年 10 月 1 日
MVCは部分的に導入できるかわからん
またMVCについても、「部分的に導入する」とかはあまりなくて、具体的には「CakePHPを採用する」といった具体的なフレームワークの採用になるわけです。そうなるとサイト作り直しになっちゃいますよね。
もっともMVCの概念についていきなり体得するということをやっているジェダイの騎士みたいな人はおそらく少数派で、ほとんどの人は「なんかよくわからんがCakePHPで書いてたらMVCになってた」が正解じゃないでしょうか。それがフレームワーク本来の使い方です。
CakePHP2 実践入門 (WEB+DB PRESS plus)
価格¥3,168
順位633,201位
著安藤 祐介, 岸田 健一郎, 新原 雅司, ほか
発行技術評論社
発売日2012 年 9 月 29 日
とにかく、概念的なものはリファクタリングに採用するにはあまりに抽象的過ぎ、かといってフレームワークを採用すると他の部分も作り直しになっちゃうわけです。
そこで、僕が考えたリファクタリング手法「ペルソナ」の出番ですよ。
ペルソナ=仮面
どんな人でも色んな仮面をかぶって生活していると思いますが、このリファクタリング手法は一つの長大なクラスを幾つもの仮面にわけていく作業です。使うのは抽象クラスだけですね。
たとえ話ですが、Meというクラスを仮定してみます。
class Me{ public $name = ''; public function work(){ $this->typing(); $this->typing(); } private function typing(){ echo 'qwerty'; } public function sleep(){ echo 'zzz…'; } }
めんどくさいんで全部書きませんが、僕という人間がやるあらゆるメソッドがこのクラスに実装されていると考えてください。この中で働いている僕はビジネスマンとしての僕なので、抽象クラスBusiness_Manというのを作成し、それをMeに継承させます。
abstract class Business_Man{ public function work(){ $this->typing(); $this->typing(); } protected function typing(){ echo 'qwerty'; } } class Me extends Business_Man{ }
抽象クラスというのは継承専用のクラスです。この手法のメリットはメソッドを分離する段階でMeの動作がほぼ保証されることです。
で、だんだん慣れて来たら、今度はメソッドのアクセシビリティを変更していきます。「よく考えたら、オフィス行くのは仕事のときだけだな」とかですね。
abstract class Business_Man{ public function work(){ $this->go_to_office(); $this->typing(); $this->typing(); } private function go_to_office(){ if(!$this->pasmo_is_ok()){ $this->charge_pasmo(); } $this->take_train(); } }
privateメソッドとprotectedメソッドの違いについては実際にクラスを作成して挙動を確認してください。なんか書いてて思ったんですけど、publicメソッドは命令で、privateとprotectedは自主的な振る舞いと考えると上手くいくんでしょうか。
さてさて、こんな感じである程度わけることができたなーと思ったら、さらに別のクラスを作成し…という感じで多段継承していきましょう。PHPは多重継承ができないので、ちょっとこの部分がダサいんですが、メソッドの整理にはとてもよい工程だと思います。
abstract class Human{} // 人間としての私 abstract class Lover extends Human{} // 恋人としての私 abstract class Father extends Lover{} // 父親としての私 abstract class Business_Man extens Father{} // 仕事人としての私 abstract class Me extends Business_Man{} //すべてを併せて私
なお、PHP5.4だったらTraitがあるので、Traitでわけていけばもっといけてるかもなーと感じたりしています。
とにかく、このペルソナ手法の特徴は以下の通り。
- 長大なクラスの動作を保証したまま動作できるので、既存のサイトにもすぐ適用できる
- しっかりと整理できてしまえば、デザインパターンの導入やTraitへの切り替えも楽(なはず)
- 抽象から具象に行くのではなく、すでに書いてしまった具象を抽象化させていくリファクタリング手法である
というわけで、興味をもった方は試してみてください。
なお、ここまで読んで「最初からMVCでやれや」と思う方もいるでしょうが、どうせ同じレベルの人が書いたらコントローラだけが肥大していく結果になるはずなので、書き散らした具象の山を抽象にわけていく過程は一度ぐらい経験した方がよいと思います。
リファクタリングにもっとも必要なものは勇気なので、この手法があなたの背中を一押しするきっかけになれば、と願います。ユニットテストが導入できないぞーとか悩んでいる人はテスト不能な PHP コードをリファクタリングするための戦略とかも読んでみてください。