برنامه نویسی شی گرا در PHP
در دنیای برنامه نویسی امروز هیچ چیز بدون نگاه شی گرایی معنا ندارد و یا اگر معنایی داشته باشد آن معنا کامل نیست. شی گرایی یکی از مفیدترین مفاهیمی است که توانسته در طول فرآیند تولید نرم افزار کمک بسیاری به تحلیل، مستند سازی و برنامه نویسی نماید.
ما می توانیم دنیای خود را به صورتی دنیایی ساخته شده از اشیا مثل ماه، زمین خورشید و ... فرض کنیم. به طور مشابه ما می توانیم خودروی خود را نیز به صورت ساخته ای از اشیا مختلف مثل چرخ دنده و ... فرض کنیم. مفاهیم برنامه نویسی شی گرا نیز به همین صورت هستند که همه چیز را به صورت یک شی می بینند و یک نرم افزار را با استفاده از اشیا مختلف پیاده سازی می کنند.
مفاهیم شی گرایی
قبل از اینکه وارد جزییات شویم بهتر است اصلاح مهم مربوط به برنامه نویسی شی گرا را تعریف کنیم.
- کلاس - یک نوع داده تعریف شده توسط برنامه نویس که شاما توابع محلی بعلاوه داده محلی است. شما می توانید کلاس را به صورت الگویی برای ساخت نمونه های زیادی از نوع یکسان اشیا در نظر بگیرید.
- شی - یک نمونه منحصربفرد از ساختار شی تعریف شده توسط یک کلاس. شما کلاس را یکبار تعریف می کنید و سپس اشیا بسیاری را که متعلق به آن هستند می سازید. اشیا همچنین به عنوان نمونه هم شناخته می شوند.
- متغیر عضو - متغیرهای تعریف شده درون کلاس. این داده در خارج از کلاس غیر قابل مشاهده بوده و می تواند توسط توابع عضو در دسترس باشند. این متغیرها در زمانی که شی ایجاد می شود صفت نامیده می شوند.
- تابع عضو- این ها توابعی هستند که درون یک کلاس تعریف می شوند و برای دسترسی به داده شی مورد استفاده قرار می گیرند.
- ارث بری - هنگامی که کلاس با ارث بردن توابع پدر تعریف می شود به عمل انجام گرفته ارث بری گفته می شود. کلاس فرزند همه یا تعدادی از توابع و متغیرهای کلاس پدر را به ارث می برد.
- کلاس پدر - کلاسی که توسط کلاس دیگر به ارث برده می شود. نام های دیگر کلاس پدر کلاس پایه یا سوپر کلاس است.
- کلاس فرزند - کلاسی که از کلاس دیگر ارث بری می کند. نام های دیگر کلاس فرزند زیرکلاس یا کلاس مشتق شده است.
- چند ریختی - این مفهوم شی گرایی برای جایی به کار می رود که یک تابع بتواند برای چند هدف مختلف استفاده شود. برای مثال نام تابع همان خواهد بود اما ممکن است تعداد مختلفی از آرگومان ها را بگیرد و بتواند وظیفه متفاوتی را انجام دهد.
- سربارگزاری - نوعی از چند ریختی که در آن تعدادی یا همه عملگرها پیاده سازی های مختلفی وابسته به انواع آرگومان های آنها دارند. به طور مشابه توابع نیز با پیاده سازی متفاوت می توانند سربارگزاری شوند.
- انتزاع داده - هر ارائه ای از داده، که در آن جزییات پیاده سازی مخفی بماند (انتزاعی شده)
- کپسوله سازی - اشاره به مفهومی دارد که در آن ما همه داده ها و توابع عضو را با هم در یک جا قرار می دهیم (کپسوله می کنیم) تا یک شی را شکل دهیم.
- سازنده - اشاره به نوع ویژه ای از یک تابع دارد که در زمانی ایجاد شی ای از کلاس به طور خودکار فراخوانی می شود.
- مخرب - اشاره به نوع ویژه ای از یک تابع دارد که در زمان پاک شدن شی به طور خودکار فراخوانی می شود.
تعریف کلاس های PHP
شکل کلی تعریف کلاس PHP
<?php class phpClass { var $var1; var $var2 = "constant string"; function myfunc ($arg1, $arg2) { [..] } [..] } ?>
شرح هر خط:
- کلمه کلیدی کلاس که بعد از آن نام کلاسی که شما می خواهید تعریف کنید می آید.
- بین براکت های باز و بسته هر تعداد از اعلان های متغیر و تعاریف تابع می آید.
- اعلان متغیر با یک کلمه کلیدی var شروع می شود که به دنبال آن نام متغیر می آید. در هنگام اعلان متغیر می توانیم آن را مقداردهی اولیه نیز کنیم.
- تعریف تابع شبیه به تعیرف توابع php است با این تفاوت که محلی کلاس هستند و برای مقداردهی و دسترسی به داده شی استفاده خواهند شد.
مثال:
در قطعه کد زیر یک کلاس از نوع Books داریم :
<?php class Books { /* Member variables */ var $price; var $title; /* Member functions */ function setPrice($par){ $this->price = $par; } function getPrice(){ echo $this->price ."<br/>"; } function setTitle($par){ $this->title = $par; } function getTitle(){ echo $this->title ." <br/>"; } } ?>
متغیر this$ یک متغیر ویژه است و اشاره به همان شی یعنی خودش دارد.
ایجاد اشیا در PHP
یکبار که شما کلاس را ایجاد کردید از آن به بعد شما می توانید هر تعداد شی که بخواهید از آن نوع کلاس ایجاد کنید. در زیر مثالی از نحوه ایجاد شی با استفاده از عملگر new آمده است:
$physics = new Books; $maths = new Books; $chemistry = new Books;
در اینجا ما سه شی ایجاد کردیم و این اشیا مستقل از یکدیگر هستند و موجودیت مجزای خودشان را دارند. سپس ما خواهیم دید چگونه به تابع عشو شی دسترسی داشته باشیم و متغیرهای عضو را پردازش کنیم.
فراخوانی تابع عضو
بعد از ایجاد اشیا، شما قادر خواهید بود تا توابع عضو مرتبط به آن شی را فراخوانی کنید. یک تابع غضو تنها قادر خواهد بود متغیر عضو تابع مرتبط را پردازش کند.
مثال زیر نشان می دهد چگونه می توانیم با فراخوانی توابع عضو عنوان و قیمت سه کتاب را مشخص کنیم.
$physics->setTitle( "Physics for High School" ); $chemistry->setTitle( "Advanced Chemistry" ); $maths->setTitle( "Algebra" ); $physics->setPrice( 10 ); $chemistry->setPrice( 15 ); $maths->setPrice( 7 );
سپس شما توابع عضو دیگر را فراخوانی می کنید تا مقادیر مشخص شده توسط مثال بالا را بدست آورید.
$physics->getTitle(); $chemistry->getTitle(); $maths->getTitle(); $physics->getPrice(); $chemistry->getPrice(); $maths->getPrice();
خروجی مثال فوق به صورت زیر خواهد بود:
Physics for High School Advanced Chemistry Algebra 10 15 7
توابع سازنده
توابع سازنده نوع ویژه ای از توابع هستند که به طور خودکار و در زمان ایجاد یک شی فراخوانی می شوند. ما می توانیم با بهره گیری از مزایای این حالب برای مقداردهی اولیه به متغیرها و یا انجام عملیاتی خاص استفاده کنیم.
PHP همچنین یک تابع به نام __construct برای تعریف یک سازنده فراهم می کند. شما می توانید به هر تعدادی که می خواهید به تالع سازنده آرگومان پاس کنید.
مثال زیر یک تابع سازنده برای کلاس Books ایجاد خواهد کرد و price و title را برای کتاب در زمان ایجاد شی مقداردهی می کند.
function __construct( $par1, $par2 ) { $this->title = $par1; $this->price = $par2; }
حالا ما نیازی به فراخوانی چند تابع مجزا برای مقداردهی price و title نداریم. ما می توانیم این ۲ متغیر عضو را در زمان ایجاد شی مقداردهی کنیم. مثال زیر را بررسی کنید:
$physics = new Books( "Physics for High School", 10 ); $maths = new Books ( "Advanced Chemistry", 15 ); $chemistry = new Books ("Algebra", 7 ); /* Get those set values */ $physics->getTitle(); $chemistry->getTitle(); $maths->getTitle(); $physics->getPrice(); $chemistry->getPrice(); $maths->getPrice();
خروجی قطعه کد فوق به این صورت خواهد بود:
Physics for High School Advanced Chemistry Algebra 10 15 7
مخرب
مشابه تابع سازنده شما می توانید یک تابع مخرب با استفاده از ()destruct__ تعریف کنید. شما می توانید همه منابع را درون یک مخرب آزاد کنید.
ارث بری
تعاریف کلاس PHP می توانند به طور اختیاری از یک تعریف کلاس پدر با استفاده از کلمه کلیدی extends ارث بری کنند. نحو نوشتاری به صورت زیر است:
class Child extends Parent { <definition body> }
تاثیر ارث بری این است که کلاس فرزند (یا زیرکلاس یا کلاس مشتق) مشخصه های زیر را دارد:
- به طور خودکار همه اعلان های متغیر کلاس پدر را دارد
- به طور خودکار همه توابع عضو را به همان شکل که در کلاس پدر هستند ارد، که (به طور پیش فرض) به همان شکلی که در کلاس پدر کار می کنند عمل می نمایند
مثال زیر از کلاس Books ارث بری کرده و عملکردهای بیشتری را بر اساس خواسته ها به آن اضافه می کند.
class Novel extends Books { var $publisher; function setPublisher($par){ $this->publisher = $par; } function getPublisher(){ echo $this->publisher. "<br />"; } }
حالا جدای از توابع ارث بری شده کلاس جدید دارای دو تابع عضو بیشتر خواهد بود.
Function Overriding
تعریف تابع در کلاس های فرزند تعاریف همنام در کلاس های پدر را override (همپوشانی) می کند. در یک کلاس فرزند، ما می توانیم تعریف تابع ارث برده شده از کلاس پدر را تغییر دهیم.
در مثال زیر توابع getPrice و getTitle همپوشانی شده اند تا مقدایری را بر گردانند.
function getPrice() { echo $this->price . "<br/>"; return $this->price; } function getTitle(){ echo $this->title . "<br/>"; return $this->title; }
اعضای عمومی
به جز مواردی که شما مشخص می کنید ویژگی ها و متدهای کلاس عمومی (public) هستند. یعنی آنها در سه موقعیت ممکن در دسترس خواهند بود:
- خارج از کلاسی که در آن اعلان شده است
- درونی کلاسی که در آن اعلان شده است
- درون کلاس دیگری که از کلاسی که در آن پیاده سازی شده اند ارث بری می کند
تا به اینجا همه اعضایی که دیدیم به صورت اعضای publicبوده اند. اگر شما بخواهید دسترسی به اعضای کلاس را محدود کنید آنگاه شما اعضای کلاس را به صورت خصوصی (private) یا محافظت شده (protected) تعریف می کنید.
اعضای خصوصی
با تعیین یک عضو به صورت خصوصی، شما دسترسی آن را به کلاسی که در آن اعلان شده محدود می کنید. عضو خصوصی نمی تواند از طریق کلاس هایی که از کلاسی که عضو در آن تعریف شده ارث بری می کنند و همچنین از خارج از آن کلاس مورد رجوع قرار گیرند.
یک عضو کلاس می تواند با استفاده از کلمه کلیدی private قبل از اسم عضو خصوصی شود.
class MyClass { private $car = "skoda"; $driver = "SRK"; function __construct($par) { // Statements here run every time // an instance of the class // is created. } function myPublicFunction() { return("I'm visible!"); } private function myPrivateFunction() { return("I'm not visible outside!"); } }
هنگامی که کلاس MyClass توسط کلاس دیگری با استفاده از کلمه کلیدی extends ارث بری می شود، ()myPublicFunction و متغیر $driver قابل مشاهده خواهند بود. کلاس ارث بری شده هیچ اطلاع و دسترسی به myPriavateFunction و متغیر $car نخواهد داشت زیرا آنها به صورت private اعلان شده اند.
اعضای حفاظت شده (protected)
ویژگی یا متد protected در کلاسی که در آن اعلان شده و همچنین کلاس هایی که از کلاسی که در آن تعریف شده ارث بری دارند قابل دسترسی است. اعضای حفاظ شده خارج از این ۲ نوع کلاس در دسترس نیستند. یک عضو کلاس می تواند با استفاده از کلمه کلیدی protected قبل از نام آن prtotected شود.
در مثال زیر گونه ای مختلف از کلاس MyClass را می بینید:
class MyClass { protected $car = "skoda"; $driver = "SRK"; function __construct($par) { // Statements here run every time // an instance of the class // is created. } function myPublicFunction() { return("I'm visible!"); } protected function myPrivateFunction() { return("I'm visible in child class!"); } }
واسط ها (interface)
واسط ها به منظور فراهم کردن امکانی جهت مشخص کردن نام توابعی مشترکی که بایستی توسط کلاس پیاده سازی شوند تعریف شده اند. پیاده ساز مختلف می توانند آن واسط را بر حسب خواسته های خود پیاده سازی کنند. شما می توانید بگویید، واسط ها اسکلتی هستند که توسط توسعه دهنده ها پیاده سازی خواهند شد.
از PHP5 به بعد این امکان فراهم شد تا واسط را به صورت زیر تعریف کنید:
interface Mail { public function sendMail(); }
سپس کلاس دیگری به صورت زیر آن واسط را پیاده سازی کند:
class Report implements Mail { // sendMail() Definition goes here }
ثابت ها
یک ثابت چیزی شبیه به یک متغیر است، که می تواند یک مقدار را در خود نگاه دارد، اما از یک نظر بیشتر شبیه به یک تابع است زیرا ثابت تغییرناپذیر است. یک بار که شما یک ثابت را تعریف کنید آن ثابت دیگر تغییر نخواهد کرد.
اعلان یک کلاس آسان است:
class MyClass { const requiredMargin = 1.7; function __construct($incomingValue) { // Statements here run every time // an instance of the class // is created. } }
در این کلاس، requiredMargin یک ثابت است. این ثابت با استفاده از کلمه کلیدی const اعلان شده است و تحت هیچ شرایطی مقدار آن بع غیر از 1.7 تغییر نخواهد کرد. توجه داشته باشید که نام ثابت پیشوند $ مشابه آنچه در مورد متغیرها به کار می رود ندارد.
کلاس های انتزاعی (Abstract Classes)\
کلاس انتزاعی، کلاسی است که نمی تواند نمونه سازی شود و تنها می توان از آن ارث بری کرد. شما یک کلاس انتراعی را با استفاده از کلمه کلیدی abstract اعلان می کنید.
هنگام ارث بری از یک کلاس abstract همه متدهایی که در کلاس پدر با کلمه abstract نشان گذاری شده اند بایستی در کلاس فرزند تعریف شوند. بعلاوه این متدها باید قابلیت مشابه یکسانی نیز داشته باشند.
abstract class MyAbstractClass { abstract function myAbstractFunction() { } }
توجه داشته باشید که تعاریف تابع درون یک کلاس abstract همچنین بایستی همراه با کلمه کلیدی abstract باشند. داشتن توابع abstract درون یک کلاس غیر abstract قانونی نیست.
کلمه کلیدی static
اعلان اعضا یا متدهای کلاس به صورت static آنها را بدون نیاز به نمونه سازی از کلاس در دسترس می کند. یک عضو اعلان شده به صورت static نمی تواند با یک شی کلاس نمونه سازی شده در دسترس باشد(البته از طریق یک متد استاتیک می تواند).
مثال:
<?php class Foo { public static $my_static = 'foo'; public function staticValue() { return self::$my_static; } } print Foo::$my_static . "\n"; $foo = new Foo(); print $foo->staticValue() . "\n"; ?>
کلمه کلیدی Final
در PHP5 کلمه کلیدی final معرفی شد که از همپوشانی یک متدی که به صورت finalتعریف شده توسط کلاس های فرزند جلوگیری می کند. اکر کلاس خودش به صورت final تعریف شود آنگاه نمی تواند ارث بری شود.
مثال زیر منجر به خطا خواهد شد:
Cannot override final method BaseClass::moreTesting()
<?php class BaseClass { public function test() { echo "BaseClass::test() called<br>"; } final public function moreTesting() { echo "BaseClass::moreTesting() called<br>"; } } class ChildClass extends BaseClass { public function moreTesting() { echo "ChildClass::moreTesting() called<br>"; } } ?>
فراخوانی متد سازنده پدر
به جای نوشتن کامل یک سازنده جدید برای زیر کلاس ها، می توانیم سازنده پدر را فراخوانی کنیم و هرچیزی که می بنیم نیاز است را به سازنده زیر کلاس اضافه کنیم. در اینجا یک مثال ساده وجود دارد:
class Name { var $_firstName; var $_lastName; function Name($first_name, $last_name) { $this->_firstName = $first_name; $this->_lastName = $last_name; } function toString() { return($this->_lastName .", " .$this->_firstName); } } class NameSub1 extends Name { var $_middleInitial; function NameSub1($first_name, $middle_initial, $last_name) { Name::Name($first_name, $last_name); $this->_middleInitial = $middle_initial; } function toString() { return(Name::toString() . " " . $this->_middleInitial); } }
در این مثال، ما یک کلاس پدر داریم (Name) که دارای یک سازنده با ۲ آرگومان است و یک زیر کلاس داریم (NameSub1) که دارای یک آرگومان با ۳ سازنده است. سازنده NameSub1 برای فراخوانی سازنده کلاس پدر (Name) به طور صریح نام کلاس پدر سپس :: و سپس لیست آرگومان ها را آورده است. به طور مشابه NameSub1 یک تابع دیگر به اسم toString دارد که تابع همانام خود در کلاس پدر را همپوشانی کرده و در آن تابع همنام کلاس پدر فراخوانی می شود.
نکته - سازنده می تواند با نام یکسان با نام کلاس می تواند تعریف شود. این نکته را در کلاس فوق می توانید ملاحظه نمایید.