オブジェクト指向あるある…
必須にしたいパラメーターを引数にしてコンストラクタに渡すのですが、引数が時に応じて数が変わると、その都度コンストラクタの違うバージョンを書かなきゃいけないという問題があります。
例えば、ここでの例を述べると、配送計画(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パターンで作られているわけですね。