今回からは、デザインパターンを使って、スマートにコーディングしてみようよ!ということで、Symfony2でも美しいコードを書いてみようということです。
今回は、PHPのクラスを用いて記載するので、Symfony2限定ということではありません、次回以降Symfony2の機能をフルパワーで発揮したデザインパターンに関して書きたいと思っています。
デザインパターンに関しては
http://www.nulab.co.jp/designPatterns/designPatterns1/designPatterns1-1.html
あたりを見るとよいです、リンク先はJavaで記載されいますが参考にはなります。要するに、オブジェクト指向の素晴らしい点をパターン化していると思ってくれればよいです。
以下のコードを具体例として挙げてみましょう
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <?phpclass LongMethod{ public $resA; public $resB; /** * very complex long method */ public function method($a, $b, $wayA, $wayB){ switch ($wayA) { case "sqrt" : $this->resA = $a ^ 2; break; case "plus" : $this->resA = $a + 10; break; case "minus" : $this->resA = $a - 10; break; default : $this->resA = $a; } $b %= 5; switch ($wayB) { case "sqrt" : $this->resB = $b ^ 2; break; case "plus" : $this->resB = $b + 10; break; case "minus" : $this->resB = $b - 10; break; default : $this->resB = $b; } }}$datas = Array( Array("a" => 1, "b" => 2, "wayA" => "minus", "wayB" => "sqrt" ), Array("a" => 3, "b" => 4, "wayA" => "plus", "wayB" => "minus" ), Array("a" => 5, "b" => 6, "wayA" => "", "wayB" => "plus" ), Array("a" => 7, "b" => 8, "wayA" => "sqrt", "wayB" => "" ));foreach ($datas as $data) { $methodClass = new LongMethod(); $methodClass->method($data["a"], $data["b"], $data["wayA"], $data["wayB"]); // print -9, 13, 5, 49 echo "resA - {$methodClass->resA}\n"; // print 4, -6, 16, 8 echo "resB - {$methodClass->resB}\n";} |
まぁこんな感じのコードを書いてみました、流れとしては
- Aに関してはWayAのパラメータで処理を分岐
- Bに関しては一旦5で割ったあまりを取得した上で、WayBのパラメータで処理を分岐
というような流れになっています。
さて、このコードの問題点は以下の点になるのではないでしょうか。
- aに関する処理とbに関する処理が単一のメソッドに集約している
- aに関する条件処理の中にアルゴリズムが含まれている
- bに関する条件処理の中にアルゴリズムが含まれている
デザインパターンはこのような問題点を解決するためにあります。
まずは問題点1を解決してみましょう
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <?phpclass MiddleMethod{ public $resA; public $resB; /** * method A * @param unknown $a * @param unknown $wayA */ public function methodA ($a, $wayA) { switch ($wayA) { case "sqrt" : $this->resA = $a ^ 2; break; case "plus" : $this->resA = $a + 10; break; case "minus" : $this->resA = $a - 10; break; default : $this->resA = $a; } } /** * method B * @param unknown $b * @param unknown $wayB */ public function methodB ($b, $wayB) { $b %= 5; switch ($wayB) { case "sqrt" : $this->resB = $b ^ 2; break; case "plus" : $this->resB = $b + 10; break; case "minus" : $this->resB = $b - 10; break; default : $this->resB = $b; } } /** * very complex long method */ public function method($a, $b, $wayA, $wayB){ methodA($a, $wayA); methodB($b, $wayB); }} |
はい、methodの中をaの処理とbの処理に分離してみました。
次に、問題点2を解決していきましょう、ここからオブジェクト指向をフル活用していきます
ストラテジパターン
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | <?phpInterface methodAInterface { public function methodA($a);}class methodASqrt implements methodAInterface { public function methodA ($a) { return $a ^ 2; }}class methodAPlus implements methodAInterface { public function methodA ($a) { return 10 + $a; }}class methodAMinus implements methodAInterface { public function methodA ($a) { return $a - 10; }}class methodADefault implements methodAInterface { public function methodA ($a) { return $a; }}class ShortAMethod{ public $resA; public $resB; /** * method A * @param unknown $a * @param unknown $wayA */ public function methodA ($a, $wayA) { $methodAClass; switch ($wayA) { case "sqrt" : $methodAClass = new methodASqrt(); break; case "plus" : $methodAClass = new methodAPlus(); break; case "minus" : $methodAClass = new methodAMinus(); break; default : $methodAClass = new methodADefault(); } $this->resA = $methodAClass->methodA($a); } /** * method B * @param unknown $b * @param unknown $wayB */ public function methodB ($b, $wayB) { $b %= 5; switch ($wayB) { case "sqrt" : $this->resB = $b ^ 2; break; case "plus" : $this->resB = $b + 10; break; case "minus" : $this->resB = $b - 10; break; default : $this->resB = $b; } } /** * very short method */ public function method($a, $b, $wayA, $wayB){ methodA($a, $wayA); methodB($b, $wayB); }} |
はい、これで、条件分岐とそれぞれの処理を分離させることができました。ここからさらに、クラス作成を分離するファクトリパターンというものも実装すればなおよいでしょう。
一応、このパターンの有用性を述べておくと、今回でいえば、Switch分のCaseの数が増加した場合の対処がしやすくなるという点です。
では、続いて問題点3を解決してみましょう、bに関する処理は、分岐する前に共通の処理が存在しています
テンプレートメソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | <?phpInterface methodAInterface { public function methodA($a);}class methodASqrt implements methodAInterface { public function methodA ($a) { return $a ^ 2; }}class methodAPlus implements methodAInterface { public function methodA ($a) { return 10 + $a; }}class methodAMinus implements methodAInterface { public function methodA ($a) { return $a - 10; }}class methodADefault implements methodAInterface { public function methodA ($a) { return $a; }}abstract class methodBAbstract { public function methodB($b) { $this->commonMethod($b); return $this->diffMethod($b); } protected function commonMethod(&$b) { $b %= 5; } abstract protected function diffMethod($b); }class methodBSqrt extends methodBAbstract { protected function diffMethod ($b) { return $b ^ 2; }}class methodBPlus extends methodBAbstract { protected function diffMethod ($b) { return $b + 10; }}class methodBMinus extends methodBAbstract { protected function diffMethod ($b) { return $b - 10; }}class methodBDefault extends methodBAbstract { protected function diffMethod ($b) { return $b; }}class ShortABMethod{ public $resA; public $resB; /** * method A * @param unknown $a * @param unknown $wayA */ public function methodA ($a, $wayA) { $methodAClass; switch ($wayA) { case "sqrt" : $methodAClass = new methodASqrt(); break; case "plus" : $methodAClass = new methodAPlus(); break; case "minus" : $methodAClass = new methodAMinus(); break; default : $methodAClass = new methodADefault(); } $this->resA = $methodAClass->methodA($a); } /** * method B * @param unknown $b * @param unknown $wayB */ public function methodB ($b, $wayB) { $methodBClass; switch ($wayB) { case "sqrt" : $methodBClass = new methodBSqrt(); break; case "plus" : $methodBClass = new methodBPlus(); break; case "minus" : $methodBClass = new methodBMinus(); break; default : $methodBClass = new methodBDefault(); } $this->resB = $methodBClass->methodB($b); } /** * very short method */ public function method($a, $b, $wayA, $wayB){ methodA($a, $wayA); methodB($b, $wayB); }} |
Abstractでクラスを発行することで、共通のメソッドを維持したまま、処理を分岐することができます。
もちろん、実際には、各クラス・インターフェースは実際には別ファイルに保存すべきです。
今回は以上となります。
投稿者プロフィール
-
中の人には主に、
PHP・Symfony2系の人と
Ruby・Rails系の人がいます。
ときどきJavascript・データベースにも手を出すかもしれません。
最新の投稿
データベース2015年2月3日Symfony2 Doctrine2の小ネタ(OneToMany,ManyToOneリレーション)
データベース2015年1月28日Symfony2 Doctrine2の小ネタ(OneToOneリレーション)
開発2015年1月21日Symfony2でもデザインパターン(PHPクラス編)
開発2014年11月26日Google検索結果画面にパンくずリストを表示する方法 (リッチスニペット対応)


