یادآوری از OOP (بخش چهارم)

(An Overview of PHP Object-Oriented Programming (Part 4

25 بهمن 1399
پروژه ساخت شبکه ی اجتماعی: یادآوری از OOP (بخش چهارم)

وراثت در PHP

مبحث امروز ما مبحث ارث بری، inheritance یا وراثت در PHP و آخرین قسمت از بخش یادآوری برنامه نویسی شیء گرا است. برای شروع کار یک فایل به نام 6_2 با پسوند php. بسازید.

مبحث وراثت در PHP همانطور که از اسمش مشخص است یعنی به ارث بردن متدها و خصوصیات (به طور کلی کدها) از یک کلاس به کلاس دیگر. شما از طریق مبحث وراثت می توانید کلاسی تعریف کنید که یک کلاس دیگر را extend کند (یعنی «گسترش» دهد). زمانی که یک کلاس، کلاس دیگری را extend می کند یعنی جزئی/زیرمجموعه ای از آن کلاس است بنابراین می تواند از کدهای آن استفاده کند. Access Modifier ای که برای این کلاس ها استفاده می شود معمولا protected است (ر.ک به جلسه قبل).

هدف ایجاد قابلیت وراثت در PHP کاهش حجم کد و جلوگیری از code duplication (نوشتن کدهای تکراری) است. در مبحث ارث بری کلاس ها می توانند از کدهای هم استفاده کنند بنابراین نیازی به نوشتن دوباره کدها نخواهیم داشت. با این حساب یک طرف قضیه کلاس پدر را داریم که کلاس اصلی است و طرف دیگر کلاس فرزند را داریم (همان کلاسی که کلاس پدر را extend می کند) که از کدهای کلاس پدر استفاده می کند بنابراین:

  • کاهش حجم کد و کم شدن پردازش کدها توسط مفسر php
  • کاهش زمان لازم برای کدنویسی و بالا رفتن سرعت تحویل پروژه ها
  • مدیریت بهتر کدها و گم نشدن در سیل عظیم چند صد خط کد!

برای اینکه کلاسی زیر مجموعه کلاسی دیگر یا فرزند آن شود باید از syntax (قالب) زیر استفاده کنید:

class ChildClassName extends ParentClassName
{
// کدهای کلاس در این قسمت قرار میگیرد
}

مثال ساده:

class Parent {
  // کدهای پدر
}
 
class Child extends Parent {
  // فرزند میتواند از کدهای کلاس پدر استفاده کند
}

حالا بیایید یک مثال عملی بزنیم.

ابتدا کلاس user همیشگی را تعریف می کنیم اما این بار برای خاصیت name یک مقدار تعیین کرده و فقط خاصیت Age را بدون مقدار می گذاریم:

 class User {
    protected $name = 'Brad';
    protected $age;
  }

سپس یک کلاس دیگر به نام customer تعریف می کنیم که مقدار پول خرج شده توسط کاربر را نشان دهد:

class Customer extends User {
  public function pay($amount){
      return $this->name . ' paid $' . $amount;
    }
}

حالا اگر از این کد خروجی بگیریم:

$customer1 = new Customer();
echo $customer1->pay(100);

ساخت شیء و اجرای متد pay برای آن باعث نمایش خروجی زیر می شود:

Brad paid $100
این شیء مستقیما درون کلاس user نبود اما از آنجا که کلاس customer گسترش دهنده کلاس user است، شیء ما هم می تواند از مقادیر آن (نام brad) استفاده کند. مفهوم Protected نیز همین است.

حالا بیایید مقدار اولیه نام را حذف کرده و با استفاده از constructor ها نام و سن را بسازیم:

 class User {
    protected $name;
    protected $age;
    
    public function __construct ($name, $age) {
      $this->name = $name;
      $this->age = $age;
        }
  }

class Customer extends User {
  public function pay($amount){
      return $this->name . ' paid $' . $amount;
    }
}

$customer1 = new Customer('john',33);
echo $customer1->pay(100);

خروجی این کد عبارت John paid $100 است.

از آنجا که customer (خریدار) جزئی از user (کاربران) سایت ما است بنابراین آن را گسترش داده یا به زبانی دیگر زیرمجموعه آن است.

حالا اگر بخواهیم خصوصیتی را برای خود کلاس customer تعیین کنیم چطور؟ این مورد، وضعیتی بسیار جالبی است. فرض کنید خصوصیتی مانند balance (موجودی حساب) داشته باشیم. این خصوصیت مخصوص یک خریدار است و مثلا برای ادمین معنی ندارد (مگر اینکه خود ادمین سایت خریدار هم باشد) بنابراین باید آن را به customer بدهیم.

از طرفی باید یک constructor را درون خود کلاس customer تعریف کنیم تا مقدار balance را هنگام ساخت شیء از آن تعیین کنیم. بنابراین می گوییم:

class Customer extends User {
    private $balance;

    public function __construct($name, $age, $balance){
      parent::__construct($name, $age);
      $this->balance = $balance;
    }
}

در اینجا ابتدا خصوصیت balance را تعریف کرده ایم. سپس یک constructor برای کلاس customer ساخته ایم؛ اگر دقت کنید این constructor متغیر های name و age را هم گرفته است! میدانید چرا؟ به این دلیل که یک customer (خریدار) اسم و سن و سال دارد و ما می خواهیم علاوه بر مقدار موجودی حساب، سن و نام او را نیز داشته باشیم.

اما نیازی به دوباره کاری نیست و تنها کافی است با استفاده از دستور (parent::__construct($name, $age همان constructor کلاس user را صدا میزنیم و مقادیر name و age را به آن می دهیم.

حالا اگر بخواهیم به خصوصیت balance دسترسی داشته باشیم باید getter تعریف کنیم (خصوصیت balance روی private است):

class Customer extends User {
    private $balance;

    public function __construct($name, $age, $balance){
      parent::__construct($name, $age);
      $this->balance = $balance;
    }

    public function pay($amount){
      return $this->name . ' paid $' . $amount;
    }

    public function getBalance(){
      return $this->balance;
    }
  }

  echo $customer1->getBalance();

خروجی این کد عدد 500 است.

موارد بالا بحث های اصلی و پایه ای در مورد وراثت بودند. کد تکمیل شده و کامل ما به شکل زیر است:

<?php
  class User {
    protected $name;
    protected $age;

    public function __construct($name, $age){
      $this->name = $name;
      $this->age = $age;
    }
  }

  class Customer extends User {
    private $balance;

    public function __construct($name, $age, $balance){
      parent::__construct($name, $age);
      $this->balance = $balance;
    }

    public function pay($amount){
      return $this->name . ' paid $' . $amount;
    }

    public function getBalance(){
      return $this->balance;
    }
  }

  $customer1 = new Customer('John', 33, 500);

  //echo $customer1->pay(100);
  echo $customer1->getBalance();

دانلود فایل کدها تا این بخش

خصوصیات و متدهای static

برای شروع کار یک فایل به نام 7_2 با پسوند php. بسازید. مقادیر متدها و خصوصیات static (به معنی «ایستا») به نمونه خاصی از یک کلاس مرتبط نیست و نسبت به خود کلاس معنی می شود. بنابراین برای استفاده از آن ها نیازی به ایجاد یک نمونه از کلاس مورد نظر نخواهیم داشت.

یکی از موقعیت هایی که این نوع خصوصیات و متدها در آن مفید خواهند بود، اعتبارسنجی رمزهای عبور است. فرآیند اعتبار سنجی رمزها یکی هستند بنابراین برای استفاده از آن نباید نیازی به ساختن یک شیء باشد. به این مثال توجه کنید:

class User {
    public $name;
    public $age;
    public static $minPassLength = 6;
}

در مثال بالا name و age به شیء ساخته شده مربوط خواهند بود (هر فرد نام و سن خاص خود را دارد) اما minPassLength (حداقل تعداد کاراکتر های رمز عبور) به فرد خاص یا شیء خاصی مربوط نیست بلکه قانونی کلی و برای تمام کاربران خواهد بود. به همین دلیل آن را static قرار داده ایم (برای دیدن مثال های بیشتر به دوره آموزشی شیء گرایی ما مراجعه کنید).

حالا می توانیم تابع این کلاس را نیز Static تعریف کنیم:

public static function validatePass($pass) {
  
}

حالا می گوییم pass$ (رمز عبور کاربر) را چک کن و مطمئن شو که بیشتر از 6 رقم باشد. اشتباهی که بسیاری از برنامه نویسان می کنند این است که چنین کدی را به شکل زیر می نویسند:

if(strlen($pass) >= $this->$minPassLength){
        return true;
      } else {
        return false;
      }

این کد کاملا اشتباه است!

شکل صحیح این کد:

if(strlen($pass) >= self::$minPassLength){
        return true;
      } else {
        return false;
      }

از آنجا که minPassLength$ یک مقدار استاتیک است، نمی توانید با کلیدواژه this$ به آن دسترسی داشته باشد بلکه باید از ::self استفاده کنید.

برای استفاده از این کد نیازی به ساختن هیچ شیء ای (مانند دستورات new user و...) نداریم. ابتدا یک رمز عبور فرضی بنویسید:

  $password = 'hello1';

حالا بیایید آن را اعتبار سنجی کنیم:

if(User::validatePass($password)){
    echo 'Password valid';
  } else {
    echo 'Password NOT valid';
  }

خروجی این کد عبارت Password NOT valid خواهد بود.

نکته: از آنجا که متد validatePass هم استاتیک است برای دسترسی به آن پس از ذکر نام کلاس از اپراتور :: استفاده کرده ایم (درست مانند خصوصیت ها). بنابراین به طور خلاصه می توان گفت که به جای this از self و به جای <- از :: استفاده می کنیم.

از جلسات بعدی وارد کدنویسی خواهیم شد. بنابراین بهتر است در مورد مدل MVC مطالعاتی داشته باشید.

مقالات کمکی:

امیدوارم از این قسمت لذت برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش ساخت شبکه اجتماعی توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.