Addded Prototype Desin Pattern
This commit is contained in:
parent
979ae42cde
commit
1cf6e3d7d2
40
code/prototype.php
Normal file
40
code/prototype.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package: patterns
|
||||
* @author: Yevhen Odynets
|
||||
* @date: 2025-07-04
|
||||
* @time: 08:13
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
use Pattern\Creational\Prototype\Author;
|
||||
use Pattern\Creational\Prototype\Page;
|
||||
|
||||
function client(): void
|
||||
{
|
||||
$author = new Author("Джордж Орвелл");
|
||||
$page = new Page(
|
||||
"Колгосп тварин",
|
||||
"Притча, сповнена гіркої іронії і сарказму.
|
||||
Трагікомічна історія спільноти тварин, що зважилися позбутися пригноблення людьми,
|
||||
і потрапила під бузувірську владу свиней.",
|
||||
$author
|
||||
);
|
||||
|
||||
// ...
|
||||
|
||||
$page->addComment("Nice book!");
|
||||
|
||||
// ...
|
||||
|
||||
$draft = clone $page;
|
||||
|
||||
echo "Dump of the clone. Note that the author is now referencing two objects.\n\n";
|
||||
|
||||
/** @noinspection ForgottenDebugOutputInspection */
|
||||
dump($draft);
|
||||
}
|
||||
|
||||
client();
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
$time_start = microtime(true);
|
||||
require '../vendor/autoload.php';
|
||||
require '../src/helpers.php';
|
||||
?><!doctype html>
|
||||
@ -19,7 +20,7 @@ require '../src/helpers.php';
|
||||
height: 100dvh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #ddd;
|
||||
background: #e0e5e5;
|
||||
color: #1c1b19;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
@ -40,22 +41,36 @@ require '../src/helpers.php';
|
||||
transform: translate(-50%, -50%);
|
||||
max-height: 92dvh;
|
||||
overflow-y: auto;
|
||||
background-color: attr(data-color type(<color>), #eef0f0);
|
||||
}
|
||||
|
||||
pre {
|
||||
pre, code {
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
pre {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
img.diagram {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.et {
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
left: .5rem;
|
||||
font-size: small;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<main data-color="#eef0f0">
|
||||
<?php require '../src/router.php' ?>
|
||||
</main>
|
||||
<div class="et">ET: <?= (microtime(true) - $time_start) ?> secs</div>
|
||||
<script>hljs.highlightAll()</script>
|
||||
</body>
|
||||
</html>
|
||||
|
27
src/Pattern/Creational/Prototype/Author.php
Normal file
27
src/Pattern/Creational/Prototype/Author.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package: patterns
|
||||
* @author: Yevhen Odynets
|
||||
* @date: 2025-07-04
|
||||
* @time: 07:54
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Pattern\Creational\Prototype;
|
||||
|
||||
class Author
|
||||
{
|
||||
/**
|
||||
* @var Page[]
|
||||
*/
|
||||
private array $pages = [];
|
||||
|
||||
public function __construct(private string $name) {}
|
||||
|
||||
public function addToPage(Page $page): void
|
||||
{
|
||||
$this->pages[] = $page;
|
||||
}
|
||||
}
|
60
src/Pattern/Creational/Prototype/Page.php
Normal file
60
src/Pattern/Creational/Prototype/Page.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package: patterns
|
||||
* @author: Yevhen Odynets
|
||||
* @date: 2025-07-04
|
||||
* @time: 07:53
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Pattern\Creational\Prototype;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Prototype.
|
||||
*/
|
||||
class Page
|
||||
{
|
||||
/** @noinspection PhpPropertyOnlyWrittenInspection */
|
||||
private array $comments = [];
|
||||
|
||||
/** @noinspection PhpPropertyOnlyWrittenInspection */
|
||||
private DateTime $date;
|
||||
|
||||
public function __construct(
|
||||
private string $title,
|
||||
private string $body,
|
||||
private readonly Author $author,
|
||||
) {
|
||||
$this->author->addToPage($this);
|
||||
$this->date = new DateTime();
|
||||
}
|
||||
|
||||
public function addComment(string $comment): void
|
||||
{
|
||||
$this->comments[] = $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* You can control what data you want to carry over to the cloned object.
|
||||
*
|
||||
* For instance, when a page is cloned:
|
||||
* - It gets a new "Copy of ..." title.
|
||||
* - The author of the page remains the same. Therefore we leave the
|
||||
* reference to the existing object while adding the cloned page to the list
|
||||
* of the author's pages.
|
||||
* - We don't carry over the comments from the old page.
|
||||
* - We also attach a new date object to the page.
|
||||
*/
|
||||
public function __clone(): void
|
||||
{
|
||||
$this->title = "КОПІЯ: " . $this->title;
|
||||
$this->body = "КОПІЯ: " . $this->body;
|
||||
$this->author->addToPage($this);
|
||||
$this->comments = [];
|
||||
$this->date = new DateTime();
|
||||
}
|
||||
}
|
165
storage/patterns_examples/builder.php
Normal file
165
storage/patterns_examples/builder.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace RefactoringGuru\Builder\RealWorld;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note that the client code uses the builder object directly. A designated
|
||||
* Director class is not necessary in this case, because the client code needs
|
||||
* different queries almost every time, so the sequence of the construction
|
||||
* steps cannot be easily reused.
|
||||
*
|
||||
* Since all our query builders create products of the same type (which is a
|
||||
* string), we can interact with all builders using their common interface.
|
||||
* Later, if we implement a new Builder class, we will be able to pass its
|
||||
* instance to the existing client code without breaking it thanks to the
|
||||
* SQLQueryBuilder interface.
|
||||
*/
|
||||
function clientCode(SQLQueryBuilder $queryBuilder)
|
||||
{
|
||||
// ...
|
||||
|
||||
$query = $queryBuilder
|
||||
->select("users", ["name", "email", "password"])
|
||||
->where("age", 18, ">")
|
||||
->where("age", 30, "<")
|
||||
->limit(10, 20)
|
||||
->getSQL();
|
||||
|
||||
echo $query;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The application selects the proper query builder type depending on a current
|
||||
* configuration or the environment settings.
|
||||
*/
|
||||
// if ($_ENV['database_type'] == 'postgres') {
|
||||
// $builder = new PostgresQueryBuilder(); } else {
|
||||
// $builder = new MysqlQueryBuilder(); }
|
||||
//
|
||||
// clientCode($builder);
|
||||
|
||||
|
||||
echo "Testing MySQL query builder:\n";
|
||||
clientCode(new MysqlQueryBuilder());
|
||||
|
||||
echo "\n\n";
|
||||
|
||||
echo "Testing PostgresSQL query builder:\n";
|
||||
clientCode(new PostgresQueryBuilder());
|
Loading…
Reference in New Issue
Block a user