مثالی ساده از Dependency Injection

در این نوشته به اختصار dependency injection را توضیح می دهم و برای آن مثالی می آورم:

dependency injection یک متد نوشتن کد است که کد ها و ارتباط بین آن ها را قابل مدیریت می کند و خواندن و فهم کد ها را آسان می سازد.

برای فهم موضوع مثالی می آوریم:
فرض کنید کلاس هایی به نام Foo، Bar و Baz داریم. خب حالا اپلیکیشن ما نیاز دارد تا از Foo استفاده کند که  Foo از Bar استفاده کرده و ‌Bar نیز کلاس Baz را صدا می زند. بطور معمول کد ما چیزی شبیه به زیر خواهد بود:

  • اپلیکیشن ما Foo را می سازد و صدا میزند.
    • در دل Foo کلاس Bar نیاز است پس اپلیکیشن کلاس Bar را صدا میزند.
      • کلاس Bar از کلاسی به نام Baz استفاده می کند که یک سرویس است پس در ‌Bar کلاس Baz را ساخته شده و کاری را انجام می دهد.

مشکل نحوه کد نویسی بالا این است که منطق و کدهای به هم تنیده وجود دارد و برای تغییر در بخشی از کد نیاز است تا بدانید کدام بخش ها به هم مرتبط بوده و گره خورده اند و به عنوان مثال اگر بخواهید بجای Bar از Wroom استفاده کنید باید کد در Foo را تغییر دهید.

حال از dependency injection استفاده می کنیم:

  • اپلیکیشن ما از Foo که از Bar استفاده می کند و ‌Bar از ‌Baz پس:
  • ابتدا Baz را می سازیم.
  • سپس Bar را ساخته و Baz را به او میدهیم
  • در نهایت Foo را میسازیم و ‌Bar را به آن می دهیم

حالا شما کنترل می کنید که کد شما از چه استفاده کند و با معکوس کردن روند ساخته شدن و صدا زده شدن منطق ها از تنیده شدن کد جلوگیری می کنید. به این الگو inversion of control گفته می شود.

و حالا یک مثال واقعی. فرض کنید برنامه ما می خواهد از دو سرویس پستی استفاده کند:

class FastCourier
{
    public function getPrice($postalCode) {// calls FastCourier web service }
}

class IRPost
{
    public function getPrice($postalCode) { // calls IRPOST web service }
}

و در اپلیکیشن ما:

class OrderService {
    public function getDeliveryPrice($user)
    {
        $courierService = new FastCourier();
         return $courierService->getPrice($store->postalCode());
}
}

حال اگر بخواهیم بجای FastCourier از IRPost استفاده کنیم باید کد getDeliveryPrice را تغییر دهیم. در این مثال شاید کاری ساده باشد اما در اپلیکیشنی بزرگ و در حال توسعه این کاری در دردسر خواهد بود و احتمال خطا را بالا می برد.

حالا کد OderService را با Dependency Injection باز نویسی می کنیم:

class OrderService
{
    private $courierService;
    public function __construct(CourierService $courierService)
    {
         $this->courierService = $courierService;
}
}

    public function getDeliveryPrice($order)
    {
          return $this->courierService->getDeliveryPrice($order->getPrice());
}
}

و با استفاده از interface سرویس های دیگر را می نویسیم:

interface ICourierService
{
    public function getprice($postalCode);
}

class FastCourier implements ICourierService
{
// Implement getPrice
}

class IRPost implements ICourierService
{
//Implement getPrice
}

حالا بدون تغییر در OrderService می توان سرویس دهنده پست را تغییر داد.
کدهایتان بی باگ ;-)