オブジェクト指向あるある…
必須にしたいパラメーターを引数にしてコンストラクタに渡すのですが、引数が時に応じて数が変わると、その都度コンストラクタの違うバージョンを書かなきゃいけないという問題があります。
例えば、ここでの例を述べると、配送計画(Delivery Plan)というオブジェクトには、IDと日付と距離は必ず入りますが、車両IDとドライバー名はある時もないときもあります。
そういう時、たいてい下記のようにコンストラクタをコピペして初期パラメーターを設定しますね。
class DeliveryPlanBuilder{ private $id; private $date; private $distance; //オプションパラメーター private $car_id; private $driver_name; function __construct() { $a = func_get_args(); $i = func_num_args(); if (method_exists($this,$f='__construct'.$i)) { call_user_func_array(array($this,$f),$a); } } public function __construct3($id, $date, $distance){ $this->id = $id; $this->date = $date; $this->distance = $distance; } public function __construct4($id, $date, $distance, $car_id){ $this->id = $id; $this->date = $date; $this->distance = $distance; $this->car_id = $car_id; } public function __construct5($id, $date, $distance, $car_id, $driver_name){ $this->id = $id; $this->date = $date; $this->distance = $distance; $this->car_id = $car_id; $this->driver_name = $driver_name; } }
まぁ、3,4個のパラメーターならコピペでもいいかもしれませんが、10個とかなってくると、いくつもコンストラクタを作らないといけないのが苦痛ですね!
ここで、デザインパターンのBuilderパターンを使うといいお!というのが、
「Effective Java」
という本の第二章の2に紹介されております。もちろん、Javaでやる方法はこの本に紹介されています。
これをPHPで実装したい、というのが今回の目的です。
こちらのStack Overflowさんを参考にさせて頂きました。m(_ _)m
https://stackoverflow.com/questions/10961673/php-builder-pattern-without-inner-classes
class DeliveryPlan{ private $id; private $date; private $distance; //オプションパラメーター private $car_id; private $driver_name; static function createBuilder($id, $date, $distance) { return new DeliveryPlanBuilder($id, $date, $distance); } function __construct(DeliveryPlanBuilder $builder){ $this->id = $builder->getId(); $this->date = $builder->getDate(); $this->distance = $builder->getDistance(); //オプションパラメーター $this->car_id = $builder->getCarId(); $this->driver_name = $builder->getDriverName(); } public function getDriverName(){ return $this->driver_name; } } class DeliveryPlanBuilder{ private $id; private $date; private $distance; //オプションパラメーター private $car_id; private $driver_name; public function __construct($id, $date, $distance){ $this->id = $id; $this->date = $date; $this->distance = $distance; } public function car_id($val){ $this->car_id = $val; return $this; } public function driver_name($val){ $this->driver_name = $val; return $this; } public function build(){ return new DeliveryPlan($this); } public function getId(){ return $this->id; } public function getDate(){ return $this->date; } public function getDistance(){ return $this->distance; } public function getCarId(){ return $this->car_id; } public function getDriverName(){ return $this->driver_name; } } $delivery_plan = DeliveryPlan::createBuilder(1, "2018/07/24", 150)->build(); var_dump($delivery_plan); $delivery_plan_with_driver_name = DeliveryPlan::createBuilder(1, "2018/07/24", 150)->driver_name("山田太郎")->build(); var_dump($delivery_plan_with_driver_name); $driver_name = $delivery_plan_with_driver_name->getDriverName(); var_dump($driver_name); //下記は必須の引数がないのでエラーになります。 $delivery_plan_test_no_parameters = DeliveryPlan::createBuilder()->build();
出力は下記の通りです。
object(DeliveryPlan)[2] private 'id' => int 1 private 'date' => string '2018/07/24' (length=10) private 'distance' => int 150 private 'car_id' => null private 'driver_name' => null object(DeliveryPlan)[3] private 'id' => int 1 private 'date' => string '2018/07/24' (length=10) private 'distance' => int 150 private 'car_id' => null private 'driver_name' => string '山田太郎' (length=12) string '山田太郎' (length=12) 後はエラー
ちなみに、私はBuilderパターン大好きで、デザインパターンのなかで一番多く使ってると思います。
作るときは面倒ですが、一度作っちゃった後のメンテナンスの容易さがたまらないです。
オブジェクト作るときに、->build()って作るのもなんとなくかっこいいのだ(`・ω・´)
Javaでも、他の言語でも、なんとかかんとかビルダー->build() ってやって作るオブジェクトは、このBuilderパターンで作られているわけですね。