本文同步更新於blog
情境:目前提供旅游行程的方式
<?php
namespace App\BuilderPattern\Vacation;
class Program
{
/**
* @return array
*/
public function getDomesticTravel()
{
//高速铁路一日体验
return [
'from' => 'Kaohsiung',
'to' => 'Taipei',
'day' => 1,
'transport' => 'High Speed Rail'
];
}
/**
* @return array
*/
public function getInternationalTravel()
{
//东京五日游
return [
'from' => 'Kaohsiung',
'to' => 'Tokyo',
'day' => 5,
'transport' => 'Airplane',
'hotel' => 'Disney Hotel'
];
}
}
老板希望我们能提供更简便的方式,来规划不同的旅游行程。
让我们用建造者模式改造它。
需求一:实作旅游行程 (产品类别)
<?php
namespace App\BuilderPattern\Vacation;
class Itinerary
{
/**
* @var string
*/
protected $from;
/**
* @var string
*/
protected $to;
/**
* @var int
*/
protected $day;
/**
* @var string
*/
protected $hotel;
/**
* @var string
*/
protected $transport;
/**
* @param string $name
* @param string|int $value
*/
public function __set($name, $value)
{
$this->$name = $value;
}
/**
* @param string $name
* @return string|int
*/
public function __get($name)
{
return $this->$name;
}
/**
* @return array
*/
public function toArray()
{
$result = get_object_vars($this);
foreach ($result as $name => $value) {
if (is_null($value)) {
unset($result[$name]);
}
}
return $result;
}
}
主要都是getter与setter方法。
当行程规划好时,我们会透过 toArray() 方法来输出。
需求二:实作行程建造者 (建造者类别)
<?php
namespace App\BuilderPattern\Vacation\Contracts;
use App\BuilderPattern\Vacation\Itinerary;
interface ItineraryPlanable
{
public function from(string $from): self;
public function to(string $to): self;
public function spendDays(int $day): self;
public function stayAt(string $hotel): self;
public function travelBy(string $transport): self;
public function getItinerary(): Itinerary;
}
<?php
namespace App\BuilderPattern\Vacation;
use App\BuilderPattern\Vacation\Itinerary;
use App\BuilderPattern\Vacation\Contracts\ItineraryPlanable;
class ItineraryBuilder implements ItineraryPlanable
{
/**
* @var Itinerary
*/
protected $itinerary;
public function __construct()
{
$this->itinerary = new Itinerary();
}
/**
* @param string $from
* @return self
*/
public function from(string $from): self
{
$this->itinerary->from = $from;
return $this;
}
/**
* @param string $to
* @return self
*/
public function to(string $to): self
{
$this->itinerary->to = $to;
return $this;
}
/**
* @param integer $day
* @return self
*/
public function spendDays(int $day): self
{
$this->itinerary->day = $day;
return $this;
}
/**
* @param string $hotel
* @return self
*/
public function stayAt(string $hotel): self
{
$this->itinerary->hotel = $hotel;
return $this;
}
/**
* @param string $transport
* @return self
*/
public function travelBy(string $transport): self
{
$this->itinerary->transport = $transport;
return $this;
}
/**
* @return Itinerary
*/
public function getItinerary(): Itinerary
{
return $this->itinerary;
}
}
行程建造者用了流式接口 (Fluent Interface),来增加程序码可读性。
我们待会会在指挥者类别中展示。
(注:此处也可以实作多个不同的行程建造者,来固定某些行程选项)
需求三:实作旅行社(指挥者类别)
<?php
namespace App\BuilderPattern\Vacation;
use App\BuilderPattern\Vacation\Contracts\ItineraryPlanable;
class TravelAgency
{
/**
* @var ItineraryPlanable
*/
protected $itineraryBuilder;
public function __construct(ItineraryPlanable $itineraryBuilder)
{
$this->itineraryBuilder = $itineraryBuilder;
}
/**
* @return array
*/
public function getHighSpeedRailItinerary()
{
$itinerary = $this->itineraryBuilder
->from('Kaohsiung')
->to('Taipei')
->travelBy('High Speed Rail')
->spendDays(1)
->getItinerary();
return $itinerary->toArray();
}
/**
* @return array
*/
public function getFiveDaysTokyoItinerary()
{
$itinerary = $this->itineraryBuilder
->from('Kaohsiung')
->to('Tokyo')
->travelBy('Airplane')
->spendDays(5)
->stayAt('Disney Hotel')
->getItinerary();
return $itinerary->toArray();
}
}
透过旅行社 (指挥者类别),我们封装了行程的实作。
使得客户端不用知道行程的建造过程。
<?php
namespace App\BuilderPattern\Vacation;
use App\BuilderPattern\Vacation\TravelAgency;
use App\BuilderPattern\Vacation\ItineraryBuilder;
class Program
{
/**
* @return array
*/
public function getDomesticTravel()
{
//高速铁路一日体验
$itineraryBuilder = new ItineraryBuilder();
$travelAgency = new TravelAgency($itineraryBuilder);
return $travelAgency->getHighSpeedRailItinerary();
}
/**
* @return array
*/
public function getInternationalTravel()
{
//东京五日游
$itineraryBuilder = new ItineraryBuilder();
$travelAgency = new TravelAgency($itineraryBuilder);
return $travelAgency->getFiveDaysTokyoItinerary();
}
}
[单一职责原则]
我们将指挥者类别、建造者类别与产品类别,视为三种不同的职责。
由旅行社指挥行程建造者来构建行程。
[开放封闭原则]
当新增/修改行程时,我们只要调整指挥者类别。
当新增/修改行程内部的逻辑时,我们仅需修改产品类别。
[依赖反转原则]
指挥者类别依赖於抽象的建造者介面。
建造者类别实作抽象的建造者介面。
最後附上类别图:
(注:若不熟悉 UML 类别图,可参考UML类别图说明。)
ʕ •ᴥ•ʔ:核心精神在於分离建造过程与产品本身的逻辑。
<<: 特权蠕变(Privilege Creep)& 自由访问控制(DAC)
本篇会围绕网路上常讲到主题,有些面试题应该也会多少考到一些,至少面试时可以讲的出来。 宣告式的渲染 ...
Colab连结 正规化 (Regularizers) 是在 Loss Function 中,多加一项...
前言 前两天我们学习了React性能优化 memo 组件记忆 useCallback 函式参考记忆 ...
Springboot AJAX ...
大家好~~欢迎来到第二十七篇 学习方向讨论 上一篇跟大家说到程序,如何自我学习,找寻方法,今天来讲别...