آشنایی با مفاهیم Constructor و Destructor در OOP


در مبحث شیئ‌گرایی و ساخت کلاس، دو مقوله داریم تحت عناوین Constructor و Destructor که در این آموزش قصد داریم با ذکر چند مثال، کاربرد آن‌ها را توضیح دهیم. واژهٔ Constructor از فعل Construct به معنی «ساختن» و واژهٔ Destructor از فعل Destruct به معنی «نابود کردن» گرفته شده و معنای تحت‌الفظی آن‌ها به ترتیب «سازنده» و «نابودکننده» است.

در مبحث شیئ‌گرایی این دو اصطلاح بدین صورت نمود عینی پیدا می‌کنند که به محض ساخت یک آبجکت از روی کلاسی خاص، مُفسر پی‌اچ‌پی کانستراکتور آن کلاس که به صورت پیش‌فرض از نظر پنهان است را به صورت خودکار فراخوانی می‌کند و در نهایت هم وقتی کار با آن کلاس به اتمام رسید، دیستراکتور فراخوانی خواهد شد. به طور کلی، هر زمانی که بخواهیم پس از ساختن یک آبجکت از روی یک کلاس تَسکی را اول از همه و آن هم به صورت خودکار انجام دهیم، باید از کانستراکتور استفاده نماییم. در مقابل کانستراکتور، مفهوم دیگری داریم تحت عنوان دیستراکتور که زمانی فراخوانی می‌شود که یک آبجکت از بین می‌رود و از جمله مواقعی که یک شیئ از بین می‌رود می‌توان به زمانی اشاره کرد که اسکریپت ما به اِتمام رسیده که بالتبع بخشی از حافظهٔ اِشغال‌شده توسط آن اسکریپت نیز آزاد می‌شود (همچنین زمانی که از متدی تحت عنوان ()unset استفاده می‌کنیم، آبجکت ساخته‌شده از بین خواهد رفت.) 

هشدار
تحت هیچ عنوان داخل کانستراکتور نمی‌توان از دستور return استفاده نمود.

در ارتباط با کانستراکتور در زبان پی‌اچ‌پی باید گفت که نوعی از متدها است که تحت عنوان Magic Method شناخته می‌شوند و این در حالی است که همچون متدها، می‌توانند پارامتر ورودی بگیرید اما در ارتباط با دیستراکتور، امکان درج هیچ گونه پارامتر ورودی را نخواهیم داشت. همچنین لازم به یادآوری است که در این زبان برای ساخت یک کانستراکتور ابتدا کیورد function را نوشته سپس دستور ()construct__ را می‌نویسیم و برای ساخت دیستراکتور نیز پس از درج کیورد function از دستور ()destruct__ استفاده می‌کنیم.

برای درک بهتر سازوکار کانستراکتور و دیستراکتور در زبان پی‌اچ‌پی، داخل پوشهٔ oop پوشه‌ای با نامی دلخواه همچون constructor-and-destructor ساخته و ساختار زیر را برای آن در نظر می‌گیریم:

constructor-and-destructor
├── classes
│   └── User.php
├── composer.json
├── index.php
└── vendor
    ├── autoload.php
    └── composer
        ├── autoload_classmap.php
        ├── autoload_namespaces.php
        ├── autoload_psr4.php
        ├── autoload_real.php
        ├── autoload_static.php
        ├── ClassLoader.php
        └── LICENSE

در واقع، محتویات این پوشه یک کپی از کدهایی است که در آموزش گذشته در پروژهٔ class-and-object ایجاد کردیم سپس فایل‌های اضافی را حذف کرده و تنها کلاس User را داخل پوشهٔ classes نگاه داشته‌‌ایم که حاوی کدهای زیر است:

<?php
namespace SokanAcademy;

class User
{
    public function __construct()
    {
        echo "Constructor called\n";
    }

    public function __destruct()
    {
        echo "Destcutor called";
    }
}

طبق نکات مطرح شده در آموزش گذشته، ابتدا به ساکن نِیم‌اِسپیس این فایل را مشخص ساخته سپس کلاسی تحت عنوان User ساخته‌ایم که داخل آن دو به اصطلاح Magic Method نوشته‌ایم. متد ()construct__ یا همان کانستراکتور این وظیفه را دارا است تا به محض ساخت یک آبجکت جدید از روی کلاس User، به صورت خودکار فراخوانی شده و استرینگی که ملاحظه می‌شود را چاپ کند و پس از پایان اجرای اسکریپتی که داخل فایل index.php خواهیم نوشت، متد ()desstruct__ یا  همان دیستراکتور فراخوانی می‌گردد. حال به فایل index.php که به منزلهٔ نقطهٔ شروع برنامه است مراجعه کرده و آن را به صورت زیر تکمیل می‌کنیم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
new SokanAcademy\User();

می‌بینیم که صرفاً کلاس User را فراخوانی کرده‌ایم و در صورت اجرای این فایل به عنوان خروجی خواهیم داشت:

/var/www/oop/constructor-and-destructor$ php index.php 
Constructor called
Destcutor called

می‌بینیم که به محض استفاده از این کلاس، کانستراکتور فراخوانی شده و دستور echo داخل آن اجرا گردیده است و پس از پایان این اسکریپت نیز که کلاس User دیگر بلااستفاده است، دیستراکتور فراخوانی شده و این آبجکت از داخل مموری حذف می‌گردد. حال به منظور درک بهتر سازوکار دیستراکتور، کدهای فوق را به صورت زیر تغییر می‌دهیم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$user = new SokanAcademy\User();
sleep(10);
unset($user);

کاری که انجام داده‌ایم این است که آبجکت ساخته‌شده از روی کلاس User را داخل متغیری تحت عنوان user$ ریخته سپس از فانکشن به اصطلاح Built-in زبان پی‌اچ‌پی تحت عنوان ()sleep استفاده کرده و در نهایت با استفاده از فانکشن ()unset آبجکت user$ را از بین برده‌ایم. در حقیقت، فانکشن ()sleep یک پارامتر ورودی در قالب ثانیه می‌گیرد و به همان میزان صبر کرده سپس ادامهٔ کدها را اجرا می‌کند به طوری که در صورت اجرای اسکریپت فوق خواهیم داشت:

/var/www/oop/constructor-and-destructor$ php index.php 
Constructor called

می‌بینیم که به محض اجرای اسکریپت فوق، کانستراکتور به صورت خودکار فراخوانی شده اما از آنجا که اسکریپت به پایان نرسیده و بالتبع آبجکت user$ از بین نرفته است،‌ دیستراکتور فراخوانی نشده بلکه پس از ۱۰ ثانیه وقفه که اجرای اسکریپت به پایان برسد کال خواهد شد.

گفتیم که کانستراکتورها همچون متدهای معمولی می‌توانند پارامتر ورودی بگیرید و همین مسئله باعث می‌گردد تا به سادگی بتوانیم پراپرتی‌های موجود در بدنهٔ کلاس را برای هر آبجکت خاص مقداردهی کنیم که برای درک بهتر این موضوع، کلاس User را به صورت زیر تکمیل می‌کنیم:

<?php
namespace SokanAcademy;

class User
{
    public $fName;
    public $lName;

    public function __construct($firstname, $lastname)
    {
        $this->fName = $firstname;
        $this->lName = $lastname;
        echo "This user`s firstname is $this->fName and lastname is $this->lName \n";
    }

    public function __destruct()
    {
        echo "Destcutor called";
    }
}

همان‌طور که ملاحظه می‌شود، دو پراپرتی تحت عناوین fName$ و lName$ در بدنهٔ کلاس خود تعریف کرده‌ایم سپس برای کانستراکتور این کلاس دو پارامتر ورودی با نام‌هایی دلخواه همچون firstname$ و lastname$ در نظر گرفته‌ایم و داخل بدنهٔ این متد نیز با استفاده از دستور this$ پراپرتی‌ها را هدف قرار داده و مقادیر آن‌ها را برابر با مقدار پارامترهای ورودی کانستراکتور قرار داده‌ایم و در نهایت آن‌ها را در قالب یک استرینگ چاپ کرده‌ایم. در این مرحله از کار، فایل index.php نیز به صورت زیر خواهد بود:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$user = new SokanAcademy\User();

که پس از اجرای آن خواهیم داشت:

PHP Fatal error:  Uncaught ArgumentCountError: Too few arguments to function SokanAcademy\User::__construct(), 0 passed in /var/www/oop/constructor-and-destructor/index.php on line 4 and exactly 2 expected in /var/www/oop/3-constructor-and-destructor/classes/User.php:8

در واقع، از آنجا که پارامترهای ورودی کانستراکتور کلاس User اجباری هستند، پس حین ساخت کلاس نیز می‌باید آن‌ها را پاس دهیم که برای رفع این ارور، کدهای فوق را به صورت زیر تکمیل می‌کنیم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$user = new SokanAcademy\User("Behzad", "Moradi");

که در صورت اجرای مجدد این اسکریپت در خروجی خواهیم دید:

This user`s firstname is Behzad and lastname is Moradi 
Destcutor called

با این توضیحات، هر زمانی که کانستراکتور موجود داخل کلاس پارامتر ورودی می‌گیرد، در حین ساخت کلاس می‌باید علائم () را پس از نام کلاس درج کرده و پارامتر/پارامترهای ورودی را پاس دهیم و در غیر این صورت، صرفاً می‌توان نام کلاس را نوشته و علائم () را حذف کرد. همچنین به صورت زیر می‌توان یک مقدار پیش‌فرض برای پارامترهای ورودی کانستراکتور در نظر گرفت تا آن‌ها را از حالت اجباری بودن خارج ساخت:

public function __construct($firstname = null, $lastname = null)
{
    $this->fName = $firstname;
    $this->lName = $lastname;
    echo "This user`s firstname is $this->fName and lastname is $this->lName \n";
}

یکی دیگر از کاربردهای کانستراکتورها در زبان پی‌اچ‌پی آن است که از طریق آن‌ها متدی را فراخوانی کنیم که برای درک بهتر این موضوع، مجدد به کلاس User باز می‌گردیم:

<?php
namespace SokanAcademy;

class User
{
    public $fName;
    public $lName;

    public function __construct($firstname, $lastname)
    {
        $this->fName = $firstname;
        $this->lName = $lastname;
        echo "This user`s firstname is $this->fName and lastname is $this->lName \n";
    }

    public function doSomething()
    {
        echo "This method is going to do something in the future. \n";
    }

    public function __destruct()
    {
        echo "Destcutor called";
    }
}

در واقع، متدی ساخته‌ایم تحت عنوان ()doSomething که این وظیفه را دارا است تا استرینگی را چاپ کند؛ سپس فایل index.php را به صورت زیر آپدیت می‌کنیم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$user = new SokanAcademy\User("Behzad", "Moradi");
$user->doSomething();

می‌بینیم که متد مذکور را روی آبجکت user$ فراخوانی کرده‌ایم به طوری که در خروجی خواهیم داشت:

This user`s firstname is Behzad and lastname is Moradi 
This method is going to do something in the future. 
Destcutor called

در واقع، گاهی پیش می‌آید که نمی‌خواهیم متد مذکور را روی آبجکت ساخته‌شده از روی آن کلاس فراخوانی کنیم بلکه تمایل داریم تا این کار به صورت خودکار صورت گیرد که برای این منظور می‌توان آن متد را به صورتی که در ادامه ملاحظه می‌شود داخل کانستراکتور فراخوانی کرد:

<?php
namespace SokanAcademy;

class User
{
    public $fName;
    public $lName;

    public function __construct($firstname, $lastname)
    {
        $this->fName = $firstname;
        $this->lName = $lastname;
        echo "This user`s firstname is $this->fName and lastname is $this->lName \n";
        $this->doSomething();
    }

    public function doSomething()
    {
        echo "This method is going to do something in the future. \n";
    }

    public function __destruct()
    {
        echo "Destcutor called";
    }
}

در حقیقت، به همان گونه‌ای که پراپرتی‌ها را داخل کانستراکتور فراخوانی کرده‌ایم، با استفاده از دستور this$ متد مذکور را کال کرده‌ایم و در ادامه نیز فایل index.php را به صورت زیر تغییر می‌دهیم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$user = new SokanAcademy\User("Behzad", "Moradi");

می‌بینیم که دیگر به صورت صریح متد مذکور را کال (فراخوانی) نکرده‌ایم و این در صورتی است که پس از اجرای این اسکریپت، پیش از هر چیز کانستراکتور اجرا شده و از آنجا که متد ()doSomething را داخل آن فراخوانی کرده‌ایم، این متد اجرا شده و استرینگ مرتبط با آن نیز در معرض دیدمان قرار خواهد گرفت و وقتی این کار با موفقیت انجام شد، دیستراکتور کال خواهد شد و آبجکت ساخته‌شده از روی کلاس User تحت عنوان user$ اصطلاحاً Destroy خواهد شد.

جمع‌بندی
به طور کلی، اگر کانستراکتور را داخل کلاس‌های خود تعریف نکنیم، این کار به صورت خودکار توسط خود مُفسر پی‌اچ‌پی صورت می‌گیرد اما در پروسهٔ‌ توسعهٔ نرم‌افزار مواقعی پیش می‌آید که نیاز داریم تا بلافاصله پس از ساخت کلاس، متد خاصی اجرا گردد و یا نیاز داریم تا یک آبجکت از کلاسی دیگر به کلاس مذکور پاس دهیم که چنین چیزی از طریق ساخت کانستراکتور به سادگی امکان‌پذیر است (لازم به یادآوری است که این قابلیت تحت عنوان Dependency Injection شناخته می‌شود که در آموزش‌‌های آتی آن را مورد بررسی قرار خواهیم داد.)

دانلود فایل‌های تمرین

لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان