آموزش مفهوم Abstract در PHP

انتزاع یکی از مفاهیم مهم در شیءگرایی است. کلاس abstract در PHP کلاسی که حداقل دارای یک متد abstract باشد. متد abstract متدی است که تعریف می‌شود ولی کدهای آن را پیاده‌سازی نمی‌کنیم. در این آموزش با یک مثال عملی با مفهوم کلاس انتزاعی در PHP آشنا می‌شویم.

پیش از این با اینترفیس در PHP آشنا شدیم. در اینترفیس، همهٔ متدها بدون پیاده‌سازی رها می‌شدند. اما در یک تشابهِ ساده، بدنهٔ برخی از متدها را در کلاس‌های abstract پیاده‌سازی می‌کنیم.

abstract در PHP

همان مثال ایجاد درگاه‌های پرداخت را به خاطر بیاورید. می‌خواهیم یک کلاس اصلی برای استانداردسازی سایر کلاس‌های درگاه پرداخت ایجاد کنیم. سپس هر درگاه جدیدی که به سیستم اضافه می‌کنیم، باید این ساختار را اجرا کند.

در بحث اینترفیس‌ها، از یک interface برای ساختار استاندارد استفاده کنیم و کلاس‌های درگاه آن را implements کردند.

<?php
interface PaymentInterface {

    public function pay($uid, $oid);
    public function verify($oid);
    public function receipt();

}

اکنون که بررسی می‌کنیم، می‌بینیم متد receipt() در تمام کلاس‌های فرزند شبیه به هم عمل می‌کنند. یعنی باید کد رهگیری و زمان پرداخت را به کاربر نمایش دهد.

بنابراین می‌خواهیم دو متد اول را به کلاس فرزند واگذار کنیم اما متد سومی را در کلاس والد (همان کلاس abstract) پیاده‌سازی کنیم. (بیشتر بدانید: اصول برنامه نویسی شیءگرا)

تعریف کلاس abstract

برای تعریف کلاس انتزاعی در PHP پیش از کلمه کلیدی class، کلمه abstract را می‌نویسیم؛ یعنی چیزی شبیه به قطعه کد زیر:

abstract class PaymentAbstract {
    // Class Body
}

اگر با تعریف کلاس آشنا نیستید، جلسه تعریف کلاس در PHP از آموزش رایگان PHP را ببینید.

مشابهاً برای تعریف متد abstract از همین کلمه پیش از تعریف سطح دسترسی متد استفاده می‌کنیم. همچنین بدنهٔ متد receipt() را می‌نویسیم:

<?php
abstract class PaymentAbstract {
    
    abstract public function pay($uid, $oid);
    abstract public function verify($oid);
    public function receipt(){
        echo "123456 (2022/08/11 11:54:31) on SabzDanesh.com";
    }
}

من در اینجا برای ساده‌شدن کدها صرفاً یک متن را echo کرده‌ام. در اصل باید پردازش‌های لازم را انجام دهیم؛ مثلاً از دیتابیس PHP اطلاعات را فراخوانی و نمایش دهیم. همینطور می‌توانیم از ویژگی‌ها (همان متغیرهای درون کلاس) یا سایر متدها نیز در پیاده‌سازی بدنهٔ متد استفاده کنیم. (جلوتر مثال می‌زنم.)

توجه کنید که در ابتدای آموزش گفتم کلاس abstract در PHP کلاسی است که حداقل یک متد abstract درون خودش داشته باشد. منظور از متد abstract متدی است که صرفاً نام و آرگومان‌های ورودی‌اش تعریف شده و هیچ بدنه‌ای ندارد.

با این توضیح، نمی‌توانیم از کلاس‌های abstract نمونه (شیء = object) ایجاد کنیم. یعنی اگر new PaymentAbstract() بنویسیم، با خطایی شبیه به زیر روبه‌رو خواهیم شد:

Fatal error:  Uncaught Error: Cannot instantiate abstract class PaymentAbstract in /home/sabzdanesh-tutorial/php-abstract.php:11

استفاده از کلاس انتزاعی PHP

برای استفاده از کلاس abstract باید از آن ارث‌بری کنیم. اگر با inheritance آشنا نیستید، جلسه ارث‌بری در PHP را حتماً ببینید. بنابراین کافی است از کلمه کلیدی extends و سپس نام کلاس انتزاعی در جلوی نام کلاسمان استفاده کنیم.

شبه‌کد زیر را برای ایجاد کلاس درگاه پرداخت فرضی (برای سامان) داریم:

class SamanPayment extends PaymentAbstract {

}

سپس متدهای abstract را در کلاس فرزند پیاده‌سازی می‌کنم:

<?php
class SamanPayment extends PaymentAbstract {
    
    public function pay($uid, $oid){
        // Method Body
    }
    public function verify($oid){
        // Method Body
    }
}

اکنون می‌توانیم از کلاس اصلی (کلاس فرزند که abstract نیست) شیء ایجاد کرده و با آن کار کنیم:

$obj = new SamanPayment();
$obj->pay(36, 10027);

نکات کار با کلاس abstract در PHP

نکته اول اینکه، ما می‌توانیم بخش‌هایی که در کلاس فرزند تکرار می‌شوند را درون کلاس abstract بالاسری قرار دهیم. در سناریوی مثالِ ما، تابع سازنده (constructor) نیز احتمالاً تکراری باشد. بنابراین آن را در کلاس انتزاعی با PHP پیاده‌سازی می‌کنیم:

<?php
abstract class PaymentAbstract {
    private $pid;

    public function __construct($payment_id=null){
        if(is_null($payment_id)){
            $this->pid = 11;  // Create new payment id
        }else{
            $this->pid = $payment_id;
        }
    }

    abstract public function pay($uid, $oid);
    abstract public function verify($oid);
    public function receipt(){
        echo "123456 (2022/08/11 11:54:31) on SabzDanesh.com";
    }
}

در متد سازنده (تابع __construct درون کلاس) اگر شناسه پرداخت تعریف شده بود (یعنی اکنون به دنبال اطلاعات پرداخت هستیم) مقدار $pid را تنظیم می‌کنیم. در حالتی که شناسه‌ای تعریف نشده (یعنی پرداخت جدید است) بایستی شناسه جدیدی تولید کنیم. (من در اینجا فقط یک عدد ثابت نوشتم که از نظر منطقی اشتباه است. ;))

اگر بخش‌هایی از این متد برایتان نامفهوم است، جلساتِ تابع در PHP و ساختار شرط if در PHP را ببینید تا با این مفاهیم مهم آشنا شوید!

(۲) چون متدهای abstract در PHP باید توسط فرزندان override شوند، سطح دسترسی آن‌ها حتماً باید public یا protected باشد. همانطور که در بحث شیءگرایی PHP می‌دانید، اگر متدی private باشد، درون فرزندان نیز در دسترس نخواهد بود.

(۳) وقتی از یک کلاس abstract ارث‌بری می‌کنید، مشابه کار با interface، حتماً باید تمام متدهای انتزاعی را پیاده‌سازی کنید؛ درغیر اینصورت خطایی مشابه زیر می‌گیرید: (خطای عدم پیاده‌سازی متد abstract در فرزند)

Fatal error: Class SamanPayment contains 2 abstract methods and must therefore be declared abstract or implement
 the remaining methods (PaymentAbstract::pay, PaymentAbstract::verify) in /home/sabzdanesh-tutorial/php-abstract.php on line 14

(۴) کلاس‌های فرزند که از کلاس انتزاعی ارث‌بری کرده‌اند را اصطلاحاً کلاس Concrete در PHP می‌نامند. البته خیلی کم از این نام استفاده می‌کنیم!

مرور تعریف کلاس abstract در PHP

به‌عنوان جمع‌بندی بحث انتزاع، موارد زیر را در مورد کلاس‌های abstract در PHP داریم:

  • کلاس Abstract باید حداقل دارای یک متد abstract باشد.
  • متد انتزاعی در PHP متدی است که هیچ بدنه‌ای ندارد و فقط نام، پارامترهای ورودی و سطح دسترسی آن مشخص شده است.
  • کلاس فرزند باید متدهای abstract را دقیقاً با همان امضا (نام و آرگومان‌های ورودی) پیاده‌سازی کند.
  • در یک نگاه انتزاعی، شاید بتوان کلاس abstract را چیزی مابین اینترفیس و کلاس در نظر گرفت.
تصویر تفاوت interface و abstraction در PHP
تصویر انتزاعی از تفاوت اینترفیس و کلاس abstract

سعی کردم مثال‌های ساده و بدون کدهای اضافی بزنم تا موضوع اصلی را درک کنید. در دوره مکمل PHP روی یک پروژه شیءگرا کار می‌کنیم تا جایگاه و نحوه استفادهٔ پیچیده‌تر از این مفاهیم را کاملاً متوجه شویم.

همچنین می‌توانید با مراجعه به مستندات انگلیسی این بحث (اینجا) قطعه کدهای بیشتری ببینید.

پیشنهاد می‌کنم یک بار تعریف کلاس Abstarct در PHP، اینترفیس و class را در ذهنتان مرور کنید تا یادگیری‌تان تثبیت شود. اگر سؤال یا تجربه‌ای دارید، در بخش دیدگاه‌ها مطرح کنید. 🙂

این آموزش بخشی از یک آموزش جامع و قدم به قدم در سبز دانش است: دوره رایگان آموزش PHP