دیزاین پترن خلاقانه یا تکوینی

الگوهای هلاقانه معمولاً ارائهدهندهٔ یکسری مکانیزمهای نمونهسازی هستند که باعث میشوند ساخت آبجکتهای مناسب در موقعیتهای مختلف آسانتر گرد
در این مقاله میخوام انواع الگوهای خلاقانه یا تکوینی رو واستون توضیح بدم:
1. Factory Dessign Pattern
این الگوی طراحی از نوع اصطلاحاً Creational است که معنای آن دقیقاً همان چیزی است که نوشته میشود؛ یعنی کلاسی است که به عنوان کارخانهٔ ساخت آبجکتها عمل مینماید. هدف اصلی این الگوی طراحی Encapsulate کردن روال ساخت آبجکت است به صورتی که بتوانید چندین کلاس مختلف را درون یک فانکشن واحد پوشش دهید. با فراهم کردن ورودی مناسب به متدی از جنس فکتوری، میتوان انتظار داشت که آبجکت صحیحی را برگرداند.
بهترین زمان برای استفاده از این الگوی طراحی وقتی است که چندین نوع متفاوت از یک موجودیت (Entity) دارید. فرض کنید یک کلاس تحت عنوان Button (دکمه) دارید و این در حالی است که این کلاس انواع مختلفی دارد مثل ImageButton یا InputButton یا FlashButton. حال بسته به موقعیتهای مختلف ممکن است بخواهید دکمههای مختلفی را ایجاد کنید و اینجا است که میتوانید از یک فکتوری (کارخانه) بخواهید که این کار زمانبَر را برایتان انجام دهد! برای روشنتر شدن این مسئله، مثالی میزنیم بدین شکل که فرض کنید سه کلاس به صورت زیر داریم:
<?php
abstract class Button {
protected $_html;
public function getHtml()
{
return $this->_html;
}
}
class ImageButton extends Button {
protected $_html = "..."; //This should be whatever HTML you want for your image-based button
}
class InputButton extends Button {
protected $_html = "..."; //This should be whatever HTML you want for your normal button (<input type="button"... />);
}
class FlashButton extends Button {
protected $_html = "..."; //This should be whatever HTML you want for your flash-based button
}
حال میتوانیم کلاس فکتوری خود را به صورت زیر ایجاد کنیم:
<?php
class ButtonFactory
{
public static function createButton($type)
{
$baseClass = 'Button';
$targetClass = ucfirst($type).$baseClass;
if (class_exists($targetClass) && is_subclass_of($targetClass, $baseClass)) {
return new $targetClass;
} else {
throw new Exception("The button type '$type' is not recognized.");
}
}
}
طریقه استفاده از این کلاس:
$buttons = array('image','input','flash');
foreach($buttons as $b) {
echo ButtonFactory::createButton($b)->getHtml()
}
خروجی اسکریپت فوق کد HTML برای ایجاد انواع مختلفی از دکمههای مورد نظر است. در واقع، با استفاده از این روش میتوانید برحسب موقعیت فعلی، دکمهای که قصد دارید بسازید را مشخص کنید.
2. Singeleton
این مورد به شما اجاره میدهد که فقط یک بار از کلاس یک آبجکت بسازید.
زمانی که ما میخواهیم فقط یک نمونه از کلاس خود را بسازیم باید دیزاین پترن سینگلتون را پیاده سازی کنیم.
مثال:
یک کانکشن ساده به دیتابیس را تصور کنید که با نمونه سازی از کلاس، کانکشن هم ساخته میشود.
بدیهی است که توسعه دهنده اشتباهاتی را مرتکب می شود که منجر به تاثیر زیادی بر سرعت پایگاه داده و سرور برنامه می شود. بگذارید یک چیز را از طریق ایجاد یک شیء متفاوت از آن کلاس ببینیم:
$dbDetails = array(
'db_name' => 'designpatterns',
'db_host' => 'localhost',
'db_user' => 'root',
'db_pass' => 'mysqldba'
);
$db1 = new databasee($dbDetails);
var_dump($db1);
$db2 = new databasee($dbDetails);
var_dump($db2);
$db3 = new databasee($dbDetails);
var_dump($db3);
$db4 = new databasee($dbDetails);
var_dump($db4);
// Output
object(database)[1]
private 'dbName' => string 'designpatterns' (length=14)
private 'dbHost' => string 'localhost' (length=9)
private 'dbPass' => string 'mysqldba' (length=8)
private 'dbUser' => string 'root' (length=4)
public 'dbh' => object(PDO)[2]
object(database)[3]
private 'dbName' => string 'designpatterns' (length=14)
private 'dbHost' => string 'localhost' (length=9)
private 'dbPass' => string 'mysqldba' (length=8)
private 'dbUser' => string 'root' (length=4)
public 'dbh' => object(PDO)[4]
object(database)[5]
private 'dbName' => string 'designpatterns' (length=14)
private 'dbHost' => string 'localhost' (length=9)
private 'dbPass' => string 'mysqldba' (length=8)
private 'dbUser' => string 'root' (length=4)
public 'dbh' => object(PDO)[6]
object(database)[7]
private 'dbName' => string 'designpatterns' (length=14)
private 'dbHost' => string 'localhost' (length=9)
private 'dbPass' => string 'mysqldba' (length=8)
private 'dbUser' => string 'root' (length=4)
public 'dbh' => object(PDO)[8]
با توجه به خروجی کد بالا، میبینید که به هر نمونه یک منبع جدید اختصاص داده شده است بنابراین هر نمونه ای که ساخته شده است یک نمونه ی جدید است، به همین خاطر به هرکدام به صورت جداگانه منابع اختصاص داده میشود.
بنابراین کد ما منابع را اشغال میکند درحالی که واقعا نیاز به این کار نیست.
راه حل :
ما باید کلاس را به نحوی تعریف کنیم که قادر به اینکه چند شی از کلاس تعریف شود را نداشته باشد.
class myDatabase {
private $dbName = null, $dbHost = null, $dbPass = null, $dbUser = null;
private static $instance = null;
private function __construct($dbDetails = array()) {
// Please note that this is Private Constructor
$this->dbName = $dbDetails['db_name'];
$this->dbHost = $dbDetails['db_host'];
$this->dbUser = $dbDetails['db_user'];
$this->dbPass = $dbDetails['db_pass'];
// Your Code here to connect to database //
$this->dbh = new PDO('mysql:host='.$this->dbHost.';dbname='.$this->dbName, $this->dbUser, $this->dbPass);
}
public static function connect($dbDetails = array()) {
// Check if instance is already exists
if(self::$instance == null) {
self::$instance = new myDatabase($dbDetails);
}
return self::$instance;
}
private function __clone() {
// Stopping Clonning of Object
}
private function __wakeup() {
// Stopping unserialize of object
}
}
کد بالا این را نشان میدهد که از ایجاد یک نمونه با کلمه کلیدی جدید جلوگیری شده است. و نیز یک متغیر استاتیک وجود دارد که مقدار متغیر ساخته شده ی قبلی را در خود نگه میدارد.
public static function connect($dbDetails = array()) {
// Check if instance is already exists
if(self::$instance == null) {
self::$instance = new myDatabase($dbDetails);
}
return self::$instance;
}