development:software-architecture:design-patterns:builder
This is an old revision of the document!
Design pattern Builder
Giả sử chúng ta có một đối tượng có rất nhiều trường cần khởi tạo, khi đó class cần khởi tạo sẽ chứa rất nhiều tham số. Như thế sẽ làm code không được đẹp và khó nâng cấp về sau.
Mẫu Builder trích xuất mã xây dựng đối tượng ra khỏi lớp của chính nó và di chuyển nó sang các đối tượng riêng biệt. Mẫu tổ chức xây dựng đối tượng thành một tập hợp các bước (buildWalls, buildDoor, v.v.). Để tạo một đối tượng, thực hiện một loạt các bước này trên một đối tượng xây dựng. Phần quan trọng là không cần phải gọi tất cả các bước. Chúng ta chỉ có thể gọi những bước cần thiết để tạo cấu hình cụ thể của một đối tượng.
- SQLQueryBuilder.php
<?php namespace OneSite\DesignPattern\Builder; /** * The Builder interface declares a set of methods to assemble an SQL query. * * All of the construction steps are returning the current builder object to * allow chaining: $builder->select(...)->where(...) */ 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; }
- MysqlQueryBuilder.php
<?php namespace OneSite\DesignPattern\Builder; /** * Each Concrete Builder corresponds to a specific SQL dialect and may implement * the builder steps a little bit differently from the others. * * This Concrete Builder can build SQL queries compatible with MySQL. */ class MysqlQueryBuilder implements SQLQueryBuilder { protected $query; protected function reset(): void { $this->query = new \stdClass; } /** * Build a base SELECT query. */ public function select(string $table, array $fields): SQLQueryBuilder { $this->reset(); $this->query->base = "SELECT " . implode(", ", $fields) . " FROM " . $table; $this->query->type = 'select'; return $this; } /** * Add a WHERE condition. */ 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; } /** * Add a LIMIT constraint. */ 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; } /** * Get the final query string. */ 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; } }
- SQLQueryBuilder.php
<?php namespace OneSite\DesignPattern\Builder; /** * This Concrete Builder is compatible with PostgreSQL. While Postgres is very * similar to Mysql, it still has several differences. To reuse the common code, * we extend it from the MySQL builder, while overriding some of the building * steps. */ class PostgresQueryBuilder extends MysqlQueryBuilder { /** * Among other things, PostgreSQL has slightly different LIMIT syntax. */ public function limit(int $start, int $offset): SQLQueryBuilder { parent::limit($start, $offset); $this->query->limit = " LIMIT " . $start . " OFFSET " . $offset; return $this; } // + tons of other overrides... }
- BuilderTest.php
<?php namespace OneSite\DesignPattern\Tests; use OneSite\DesignPattern\Builder\MysqlQueryBuilder; use OneSite\DesignPattern\Builder\PostgresQueryBuilder; use OneSite\DesignPattern\Builder\SQLQueryBuilder; use PHPUnit\Framework\TestCase; /** * Class BuilderTest * @package OneSite\DesignPattern\Tests */ class BuilderTest extends TestCase { /** * */ public function testBuilderPattern() { $this->clientCode(new MysqlQueryBuilder()); $this->clientCode(new PostgresQueryBuilder()); return $this->assertTrue(true); } /** * @param SQLQueryBuilder $queryBuilder */ private function clientCode(SQLQueryBuilder $queryBuilder) { $query = $queryBuilder ->select("users", ["name", "email", "password"]) ->where("age", 18, ">") ->where("age", 30, "<") ->limit(10, 20) ->getSQL(); echo $query; } }
Tham khảo
development/software-architecture/design-patterns/builder.1722927590.txt.gz · Last modified: 2024/08/06 06:59 by 127.0.0.1