سرفصل‌های آموزشی
آموزش OOP در PHP
پروژهٔ ساخت ماشین حساب بر پایهٔ متودولوژی OOP در زبان PHP

پروژهٔ ساخت ماشین حساب بر پایهٔ متودولوژی OOP در زبان PHP

در دورهٔ «آموزش 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، می‌توان این ابزار را نصب نموده و مستندات این پروژه را بر اساس کامنت‌هایی که در سورس‌کد درج نمودیم بسازیم.

online-support-icon