建造者模式,也可以叫做生成器模式,是一种创建型设计模式,使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。
从建筑上来说,盖房子不是一下子就马上能把一个房子盖好的,而是通过一砖一瓦搭建出来的。一个房子不仅有砖瓦,还有各种管道,各种电线等等,由它们各个不部分共同组成了一栋房子。可以说,建造者模式就是这样非常形象的由各种部件来组成一个对象(房子)的过程。
建造者模式一般认为有四个角色:
- 产品角色,产品角色定义自身的组成属性
- 抽象建造者,抽象建造者定义了产品的创建过程以及如何返回一个产品
- 具体建造者,具体建造者实现了抽象建造者创建产品过程的方法,给产品的具体属性进行赋值定义
- 指挥者,指挥者负责与调用客户端交互,决定创建什么样的产品
优点:
生成不同形式的产品时, 你可以复用相同的制造代码。
单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来。
缺点:
由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。
代码:
<?php
// 产品角色
class User{
protected $name = '';
protected $age = '';
protected $sex = '';
public function setName($name){
$this->name = $name;
}
public function setAge($age){
$this->age = $age;
}
public function setSex($sex){
$this->sex = $sex;
}
public function getUser(){
echo '这个人姓名是:' . $this->name
. ',年龄是:' . $this->age
. ',性别是:' . $this->sex .'。<br>';
}
}
// 抽象建造者
interface UserBuilder{
public function builderName();
public function builderAge();
public function builderSex();
public function getUser();
}
// 具体建造者
class CommonBuilder implements UserBuilder{
public $user;
public function __construct(){
$this->user = new User();
}
public function builderName(){
$this->user->setName('普通用户');
}
public function builderAge(){
$this->user->setAge('20');
}
public function builderSex(){
$this->user->setSex('男');
}
public function getUser(){
return $this->user;
}
}
// 具体建造者
class SupperBuilder implements UserBuilder{
public $user;
public function __construct(){
$this->user = new User();
}
public function builderName(){
$this->user->setName('超级用户');
}
public function builderAge(){
$this->user->setAge('30');
}
public function builderSex(){
$this->user->setSex('女');
}
public function getUser(){
return $this->user;
}
}
// 指挥者
class UserDirector{
public function make(UserBuilder $builder){
$builder->builderName();
$builder->builderAge();
$builder->builderSex();
return $builder->getUser();
}
}
$director = new UserDirector();
$commonBuilder = new CommonBuilder();
$commonUser = $director->make($commonBuilder);
$commonUser->getUser();
$supperBuilder = new SupperBuilder();
$supperUser = $director->make($supperBuilder );
$supperUser->getUser();
建造者模式最好的应用方式之一是 SQL 查询生成器。 生成器接口定义了生成一般 SQL 查询所需的通用步骤。 另一方面, 对应不同 SQL 语言的具体生成器会去实现这些步骤, 返回能在特定数据库引擎中执行的 SQL 查询语句。
<?php
interface SQLQueryBuilder
{
public function select(string $table, array $fields): SQLQueryBuilder;
public function where(string $field, string $value, string $operator = '='): SQLQueryBuilder;
public function limit(int $start, int $offset): SQLQueryBuilder;
// +100 other SQL syntax methods...
public function getSQL(): string;
}
class MysqlQueryBuilder implements SQLQueryBuilder
{
protected $query;
protected function reset(): void
{
$this->query = new \stdClass();
}
public function select(string $table, array $fields): SQLQueryBuilder
{
$this->reset();
$this->query->base = "SELECT " . implode(", ", $fields) . " FROM " . $table;
$this->query->type = 'select';
return $this;
}
public function where(string $field, string $value, string $operator = '='): SQLQueryBuilder
{
if (!in_array($this->query->type, ['select', 'update', 'delete'])) {
throw new \Exception("WHERE can only be added to SELECT, UPDATE OR DELETE");
}
$this->query->where[] = "$field $operator '$value'";
return $this;
}
public function limit(int $start, int $offset): SQLQueryBuilder
{
if (!in_array($this->query->type, ['select'])) {
throw new \Exception("LIMIT can only be added to SELECT");
}
$this->query->limit = " LIMIT " . $start . ", " . $offset;
return $this;
}
public function getSQL(): string
{
$query = $this->query;
$sql = $query->base;
if (!empty($query->where)) {
$sql .= " WHERE " . implode(' AND ', $query->where);
}
if (isset($query->limit)) {
$sql .= $query->limit;
}
$sql .= ";";
return $sql;
}
}
class PostgresQueryBuilder extends MysqlQueryBuilder
{
public function limit(int $start, int $offset): SQLQueryBuilder
{
parent::limit($start, $offset);
$this->query->limit = " LIMIT " . $start . " OFFSET " . $offset;
return $this;
}
}
class app{
public function make(SQLQueryBuilder $queryBuilder)
{
$query = $queryBuilder
->select("users", ["name", "email", "password"])
->where("age", 18, ">")
->where("age", 30, "<")
->limit(10, 20)
->getSQL();
echo $query,PHP_EOL;
}
}
$app = new app();
$app->make(new MysqlQueryBuilder());
$app->make(new PostgresQueryBuilder());