در دورهٔ «آموزش OOP در PHP» سعی شد تا مثالهای مورد استفاده تا حد ممکن نزدیک به یک سری Use Case در دنیای واقعی توسعهٔ نرمافزار باشند اما در عین حال در برخی موارد ناگزیر بودیم تا مباحث را به صورت تئوریک بیان کنیم. در همین راستا، در آموزش آخر از این دوره قصد داریم تا یک ماشین حساب ساده بر پایهٔ متودولوژی شیئگرا توسعه داده و برخی از مباحث مورد بحث در این دوره را در آن استفاده نماییم.
برای همین منظور، داخل پوشهٔ oop
پروژهای با نامی دلخواه همچون object-oriented-calculator
ساخته و ساختار پروژهای که در طول این دوره مورد استفاده قرار دادیم را داخلش ایجاد میکنیم. برای شروع، در مسیر روت پروژه فولدری تحت عنوان interfaces
ساخته و داخل آن فایلی به نام OperatorInterface.php
میسازیم و کدهای زیر را داخل آن وارد میکنیم:
<?php
namespace Intrfc;
/**
* A base blueprint for mathematical operation classes.
*/
interface OperatorInterface
{
/**
* @param int $no The numbers which the operation will apply on
* @param int $result The number which holds the previous result
* @return int|float
*/
public function run(int $no, int $result);
}
همانطور که ملاحظه میشود، نِیماِسپیسی تحت عنوان Intrfc
برای این فایل در نظر گرفتهایم که در ادامهٔ آموزش خواهیم دید به چه شکل آن را در فایل composer.json
معرفی خواهیم نمود. سپس با استفاده از کیورد interface
اینترفیسی تحت عنوان OperatorInterface
ساختهایم که به منزلهٔ یک نقشهٔ کلی برای کلاسهای مرتبط با چهار عمل اصلی است که میباید بر آن اساس ساخته شوند. داخل این اینترفیس یک متد نوشتهایم تحت عنوان ()run
که دو پارامتر ورودی میگیرد به نامهای no$
و result$
که بر اساس نکات مطرحشده در آموزش آشنایی با مفهوم Type Hinting در زبان PHP، نوع دیتا تایپ آنها را int
در نظر گرفتهایم تا توسعهدهنده موظف به پاس دادن فقط و فقط عددهای صحیح گردد.
همچنین میبینیم که به چه شکل با استفاده از PHPDoc کامنتهایی هم در سطح اینترفیس و هم در سطح متد درج نمودهایم تا در نهایت بتوانیم مستنداتی نیز برای این پروژه بسازیم. به طور مثال، در کامنت موجود برای متد ()run
از تگ param@
استفاده که برای معرفی پارامترهای ورودی استفاده میشود سپس تایپ این پارامتر که int
است را نوشته و در نهایت نام پارامتر که no$
است را نوشته و در ادامه توضیحاتی مرتبط آوردهایم. به همین منوال، به معرفی پارامتر result$
نیز پرداخته و در نهایت با استفاده از تگ return@
نشان دادهایم که خروجی این متد میتواند عدد صحیح یا عدد اعشاری باشد. در ادامه، پوشهٔ دیگری به نام classes
ساخته که میباید حاوی فایلهای زیر باشد:
classes
├── Adder.php
├── Calculator.php
├── Divider.php
├── Multiplier.php
└── Subtractor.php
ابتدا اقدام به تکمیل فایل Adder.php
میکنیم:
<?php
namespace App;
use \Intrfc\OperatorInterface as OI;
/**
* This class implements the CacheInterface and has only one method to do the + operation
*/
final class Adder implements OI
{
public function run(int $no, int $result)
{
return $result + $no;
}
}
نِیماِسپیسی که برای فایلهای موجود در این پوشه در نظر گرفتهایم App
است. در ادامه و در خط چهارم میبینیم که از دستور use
استفاده کرده و مسیر اینترفیسی که پیش از ساختیم را داده اما برای آن نامی مستعار همچون OI
انتخاب کردهایم تا کوتاهتر اما در عین حال بامسمی باشد.
همانطور که ملاحظه میشود، کلاسی از جنس final
ساختهایم به نام Adder
که از اینترفیس OperatorInterface
ایمپلیمنت شده است و از این جهت این کلاس را final
کردهایم چرا که قصد نداریم تا دیگر کلاسها بتوانند از این کلاس ارثبری کنند و قبل از این کلاس نیز از یک بلوک کامنت به منظور معرفی آن استفاده نمودهایم. با توجه به این که کلاس Adder
از اینترفیس OperatorInterface
ایمپلیمنت شده است، بالتبع میباید حاوی متدی تحت عنوان ()run
باشد که پس از نوشتن این متد و تکمیل بدنهٔ آن، میبینیم که دستور دادهایم تا از طریق این متد پراپرتی result$
با پراپرتی no$
جمع گردد و مقدار نهایی ریترن گردد. به همین منوال، اقدام به تکمیل فایل Subtractor.php
میکنیم:
<?php
namespace App;
use \Intrfc\OperatorInterface as OI;
/**
* This class implements the CacheInterface and has only one method to do the - operation
*/
final class Subtractor implements OI
{
public function run(int $no, int $result)
{
return $result - $no;
}
}
میبینیم که ساختار کاملاً مشابه کلاس Adder
است با این تفاوت که داخل بدنهٔ متد ()run
از علامت -
استفاده نمودهایم. برای فایل Divider.php
نیز خواهیم داشت:
<?php
namespace App;
use \Intrfc\OperatorInterface as OI;
/**
* This class implements the CacheInterface and has only one method to do the / operation
*/
final class Divider implements OI
{
public function run(int $no, int $result)
{
return $result / $no;
}
}
و به همین صورت، فایل Multiplier.php
را به صورت زیر تکمیل میکنیم:
<?php
namespace App;
use \Intrfc\OperatorInterface as OI;
/**
* This class implements the CacheInterface and has only one method to do the * operation
*/
final class Multiplier implements OI
{
public function run(int $no, int $result)
{
return $result * $no;
}
}
حال نوبت به ساخت کلاسی تحت عنوان Calculator
میرسد که به منزلهٔ یکی از کلاسهای اصلی این پروژه است؛ برای همین منظور، فایلی به نام Calculator.php
ساخته و آن را به صورت زیر تکمیل میکنیم:
<?php
namespace App;
/**
* The Calculator class in used in index.php to pass the type of operation to and return the result
*/
class Calculator
{
/**
* @var int $result Contains the result of all operations
*/
protected $result = 0;
/**
* @var \Intrfc\OperatorInterface $operation Contains the type of operation including Adder, Subtractor, Divider and Multiplier classes
*/
protected $operation;
/**
* @param \Intrfc\OperatorInterface $operation is the type of mathematical operation
* @return void
*/
public function setOpration(\Intrfc\OperatorInterface $operation)
{
$this->operation = $operation;
}
/**
* This is the main method that calculates the result
*
* @return void
*/
public function calculate()
{
foreach (func_get_args() as $number) {
$this->result = $this->operation->run($number, $this->result);
}
}
/**
* This is the method that returns the value of $result property on call
*
* @return int The final value of $result property
*/
public function getResult()
{
return $this->result . "\n";
}
}
داخل بدنهٔ این کلاس دو پراپرتی داریم تحت عناوین result$
با مقدار اولیهٔ ۰ و همچنین operation$
و همانطور که از کامنتهای این دو پراپرتی مشخص است، قرار است تا به ترتیب نتیجهٔ نهایت و نوع عملیات ریاضیاتی را در خود ذخیره سازند.
در ادامه، متدی از جنس Setter نوشتهایم به نام ()setOperation
که یک پارامتر ورودی به نام operation$
میگیرد که حتماً میباید از جنس اینترفیس OperatorInterface
باشد و داخل این متد هم پراپرتی operation$
که در بدنهٔ این کلاس تعریف نمودیم را مقداردهی کردهایم (با توجه به این که متد فوق هیچ چیزی را ریترن نمیکند، در کامنت مرتبط با این متد مقدار void
را برای تگ return@
در نظر گرفتهایم.)
متد دیگری داریم به نام ()calculate
که از طریق این متد پراپرتی result$
مقداردهی میشود بدین شکل که داخل بدنهٔ این متد از یک حلقه استفاده کرده و به عنوان آرگومان ورودی این حلقه از فانکشن تعبیهشده در زبان پیاچپی به نام ()func_get_args
استفاده کرده که این وظیفه را دارا است تا کلیهٔ آرگومانهای ورودی یک متد شناسایی کند. محتویات این فانکشن را داخل متغیری تحت عنوان number$
ریخته سپس داخل این حلقه مقدار پراپرتی result$
را برابر با فراخوانی متد ()run
منتسب به پراپرتی operation$
قرار دادهایم به طوری که به عنوان آرگومان اول این متد از متغیر number$
استفاده کرده و به عنوان آرگومان دوم نیز پراپرتی result$
را مورد استفاده قرار دادهایم.
در نهایت هم یک متد از جنس Getter نوشتهایم به نام ()getResult
که همانطور که از کامنتها مشخص است، میتواند یک عدد صحیح ریترن کند و داخل بدنهٔ این متد نیز مقدار نهایی پراپرتی result$
را ریترن کردهایم. حال نوبت آپدیت کردن فایل composer.json
میرسد تا از آن طریق نِیماِسپیسهای Intrfc
و App
را داخل آن تعریف نماییم به طوری که داریم:
{
"autoload": {
"psr-4": {
"App\\": "classes",
"Intrfc\\": "interfaces"
}
}
}
همانطور که ملاحظه میشود، نِیماِسپیسهای فوقالذکر را تعریف نموده و مسیری که فایلهایی با چنین نِیماِسپیسهایی در آن تعریف شدهاند را نیز مشخص ساختهایم و اکنون نیاز است تا دستور زیر را اجرا کنیم:
/var/www/oop/object-oriented-calculator$ composer dump-autoload -o
Generated optimized autoload files containing 6 classes
میبینیم که این پروسه با موفقیت انجام شد و از این پس به کلیهٔ کلاسها و اینترفیسی که تاکنون تعریف نمودهایم از طریق قابلیت اُوتولودینگ پیاچپی دسترسی خواهیم داشت. همانطور که در طول این دوره مشاهده شد، فایلی تحت عنوان index.php
به منزلهٔ نقطهٔ شروع برنامه است و از همین روی آن را به صورت زیر تکمیل میکنیم:
<?php
ini_set('display_errors', '1');
require_once "vendor/autoload.php";
use App\Adder;
use App\Calculator;
use App\Divider;
use App\Multiplier;
use App\Subtractor;
$cal = new Calculator();
$cal->setOpration(new Adder());
$cal->calculate(50, 50);
echo $cal->getResult();
$cal->setOpration(new Subtractor());
$cal->calculate(20);
echo $cal->getResult();
$cal->setOpration(new Divider());
$cal->calculate(2);
echo $cal->getResult();
$cal->setOpration(new Multiplier());
$cal->calculate(4);
echo $cal->getResult();
در خط دوم دستور دادهایم تا هر گونه ارور و اِکسپشنی که رخ داد، در معرض دیدمان قرار گیرد تا راحتتر بتوانیم اقدام به دیباگ کردن این پروژه نماییم و در خط سوم هم فایل autoload.php
در پوشهٔ vendor
را ایمپورت کردهایم که این وظیفه را دارا است تا هر کلاسی که استفاده نمودیم را به صورت خودکار ایمپورت کند. در ادامه، برای آن که سورسکد تمیزتر به نظر آید، با استفاده از یک سری دستور use
اقدام به معرفی کلاسهایی کردهایم که قصد استفاده از آنها را داریم تا در ادامه که قصد داریم نام آنها را بنویسیم، دیگر نیازی به درج نِیماِسپیس مرتبط با آنها نباشد.
در خط یازدهم آبجکتی تحت عنوان cal$
از روی کلاس Calculator
ساختهایم سپس میبینیم که چگونه عملیات چهار عمل اصلی را با استفاده از این آبجکت انجام دادهایم. به طور مثال، در تفسیر خطوط سیزدهم تا پانزدهم که مرتبط با عملیات جمع است میتوان گفت که ابتدا به ساکن با فراخونی متد ()setOperation
و پاس دادن آبجکتی از روی کلاس Adder
، این دستور را دادهایم که پراپرتی operation$
موجود در بدنهٔ کلاس Calculator
مرتبط با عمل اصلی جمع گردد سپس متد ()calculate
را فراخوانی کرده و دو عددی که قصد داریم با یکدیگر جمع کنیم را به عنوان آرگومان اول و دوم این متد در نظر گرفتهایم و در نهایت هم مقداری که متدی ()getResult
ریترن میکند را چاپ کردهایم. به همین منوال، علمیات تفریق، تقسیم و ضرب را نیز تکمیل نمودهایم و جهت تست، دستور زیر را در محیط کامندلاین اجرا کرده و یا این فایل را داخل مرورگر اجرا میکنیم:
/var/www/oop/object-oriented-calculator$ php index.php
100
80
40
160
در تفسیر خروجی فوق باید گفت که در پروسهٔ انجام عملیات جمع، اعداد ۵۰ با ۵۰ با یکدیگر جمع میشوند که حاصل نهایی میشود ۱۰۰ و در ادامه و با انجام عملیات تفریق، عدد ۲۰ از نتیجه قبلی کم میشود که حال میشود ۸۰ و طی عملیات تقسیم نیز عدد ۸۰ بر ۲ تقسیم میشود که باقیمانده میشود ۴۰ و در نهایت این عدد در ۴ ضرب میگردد که نتیجهٔ نهایی میشود ۱۶۰.
در پایان نیز با توجه آموزش آشنایی با استاندارد کامنتگذاری PHPDoc، میتوان این ابزار را نصب نموده و مستندات این پروژه را بر اساس کامنتهایی که در سورسکد درج نمودیم بسازیم.