今回からは、デザインパターンを使って、スマートにコーディングしてみようよ!ということで、Symfony2でも美しいコードを書いてみようということです。
今回は、PHPのクラスを用いて記載するので、Symfony2限定ということではありません、次回以降Symfony2の機能をフルパワーで発揮したデザインパターンに関して書きたいと思っています。
デザインパターンに関しては
http://www.nulab.co.jp/designPatterns/designPatterns1/designPatterns1-1.html
あたりを見るとよいです、リンク先はJavaで記載されいますが参考にはなります。要するに、オブジェクト指向の素晴らしい点をパターン化していると思ってくれればよいです。
以下のコードを具体例として挙げてみましょう
<?php
class 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を解決してみましょう
<?php
class 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を解決していきましょう、ここからオブジェクト指向をフル活用していきます
ストラテジパターン
<?php
Interface 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に関する処理は、分岐する前に共通の処理が存在しています
テンプレートメソッド
<?php
Interface 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検索結果画面にパンくずリストを表示する方法 (リッチスニペット対応)


