سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

CMS نویس مرجعی برای آموزش برنامه نویسی وب و PHP، توسعه افزونه‌های اختصاصی وردپرس، طراحی ورک‌فلو و اتوماسیون با n8n و توسعه CMS های اختصاصی است. هدف ما ارائه آموزش‌های کاربردی و راهکارهای حرفه‌ای برای توسعه‌دهندگان، کسب‌وکارها و علاقه‌مندان به دنیای برنامه‌نویسی است.

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
Proxy Pattern در PHP چیست؟ آموزش الگوی Proxy با مثال عملی

Proxy Pattern در PHP چیست؟ آموزش الگوی Proxy با مثال عملی

ممدیر کل سیستم ۲۵ شهریور ۱۴۰۵ ۱۹ دقیقه مطالعه ۳ بازدید
Proxy Pattern در PHP چیست؟ آموزش الگوی Proxy با مثال عملی

در پروژه‌های PHP و Laravel گاهی لازم است قبل از اجرای یک عملیات، بررسی یا پردازش خاصی انجام شود. برای مثال:

  • بررسی کنیم کاربر اجازه دسترسی به یک سرویس را دارد یا خیر.
  • نتیجه یک درخواست را در Cache ذخیره کنیم.
  • ایجاد یک Object سنگین را تا زمان نیاز به تأخیر بیندازیم.
  • زمان اجرای یک سرویس را ثبت کنیم.
  • قبل از ارسال درخواست به API، اعتبارسنجی انجام دهیم.
  • دسترسی به یک سرویس خارجی یا حساس را محدود کنیم.

اگر این منطق را مستقیماً داخل کلاس اصلی قرار دهیم، ممکن است کلاس ما بیش از حد بزرگ و وابسته شود. همچنین اگر منطق کنترل دسترسی یا Cache را در تمام Clientها تکرار کنیم، نگهداری کد دشوار خواهد شد.

اینجا Proxy Pattern می‌تواند راه‌حل مناسبی باشد.

الگوی Proxy یکی از الگوهای ساختاری یا Structural Design Patterns است که یک Object واسط را بین Client و Object اصلی قرار می‌دهد. این واسط معمولاً همان Interface شیء اصلی را پیاده‌سازی می‌کند و درخواست‌ها را پس از انجام پردازش‌های لازم به Object واقعی منتقل می‌کند. <Cite refs={["turn0search0","turn0search1"]} />

Proxy Pattern چیست؟

در الگوی Proxy، به جای اینکه Client مستقیماً با Object اصلی ارتباط داشته باشد، با یک Object جایگزین یا واسط کار می‌کند.

این Proxy می‌تواند قبل یا بعد از اجرای متد اصلی، عملیات دیگری انجام دهد؛ برای مثال:

  • بررسی مجوز دسترسی
  • ثبت Log
  • ذخیره نتیجه در Cache
  • ایجاد تنبل یا Lazy Object
  • محدودکردن تعداد درخواست‌ها
  • ارسال درخواست به یک سرویس Remote
  • اعتبارسنجی ورودی‌ها

نکته مهم این است که Proxy معمولاً همان Interface مربوط به Object اصلی را پیاده‌سازی می‌کند. بنابراین Client لازم نیست بداند که در حال کار با Object واقعی است یا Proxy. <Cite refs={["turn0search0","turn0search1"]} />

به زبان ساده:


Client → Proxy → Real Subject

Client فقط متد موردنظر را صدا می‌زند. Proxy تصمیم می‌گیرد که آیا درخواست باید اجرا شود، از Cache پاسخ داده شود یا به Object اصلی منتقل شود.

اجزای اصلی Proxy Pattern

الگوی Proxy معمولاً از چهار بخش تشکیل می‌شود.

۱. Subject

Subject یک Interface است که متدهای مشترک بین Proxy و Object اصلی را تعریف می‌کند.

۲. Real Subject

Real Subject همان کلاس اصلی است که منطق واقعی برنامه را اجرا می‌کند.

۳. Proxy

Proxy همان واسطی است که Interface مشترک را پیاده‌سازی می‌کند و به Real Subject دسترسی دارد.

۴. Client

Client بخشی از برنامه است که از Subject استفاده می‌کند. Client نباید به نوع واقعی Object وابسته باشد.

ساختار کلی:


             ┌──────────────┐

             │    Client    │

             └──────┬───────┘

                    │

                    ▼

             ┌──────────────┐

             │    Proxy     │

             └──────┬───────┘

                    │

                    ▼

             ┌──────────────┐

             │ Real Subject │

             └──────────────┘

مثال ساده Proxy Pattern در PHP

فرض کنید یک سیستم نمایش تصویر داریم. بارگذاری تصویر ممکن است عملیاتی پرهزینه باشد؛ بنابراین نمی‌خواهیم تصویر از همان لحظه ساخت Object بارگذاری شود.

ابتدا یک Interface تعریف می‌کنیم:


<?php



declare(strict_types=1);



interface ImageInterface

{

    public function display(): void;

}

این Interface متد display را مشخص می‌کند.

ساخت Real Subject

کلاس RealImage مسئول بارگذاری واقعی تصویر است:


<?php



declare(strict_types=1);



final class RealImage implements ImageInterface

{

    public function __construct(

        private readonly string $filename

    ) {

        $this->loadFromDisk();

    }



    private function loadFromDisk(): void

    {

        echo "Loading image: {$this->filename}" . PHP_EOL;

    }



    public function display(): void

    {

        echo "Displaying image: {$this->filename}" . PHP_EOL;

    }

}

در Constructor، تصویر از دیسک بارگذاری می‌شود. در یک پروژه واقعی، این عملیات می‌تواند شامل خواندن فایل، پردازش تصویر یا دریافت اطلاعات از Storage باشد.

ساخت Proxy

حالا یک Proxy می‌سازیم که از بارگذاری تصویر تا زمان نیاز جلوگیری می‌کند:


<?php



declare(strict_types=1);



final class ImageProxy implements ImageInterface

{

    private ?RealImage $realImage = null;



    public function __construct(

        private readonly string $filename

    ) {

    }



    public function display(): void

    {

        if ($this->realImage === null) {

            $this->realImage = new RealImage($this->filename);

        }



        $this->realImage->display();

    }

}

در اینجا تصویر هنگام ساخت ImageProxy بارگذاری نمی‌شود. فقط زمانی که متد display اجرا شود، Object واقعی ساخته خواهد شد.

استفاده از Proxy


<?php



$image = new ImageProxy('products/laptop.jpg');



echo "Proxy created." . PHP_EOL;



$image->display();



echo "Displaying again." . PHP_EOL;



$image->display();

خروجی نمونه:


Proxy created.

Loading image: products/laptop.jpg

Displaying image: products/laptop.jpg

Displaying again.

Displaying image: products/laptop.jpg

همان‌طور که مشاهده می‌کنید، پیام Loading image فقط یک بار نمایش داده می‌شود. Proxy Object واقعی را فقط در اولین اجرای display ایجاد می‌کند.

این نوع Proxy را معمولاً Virtual Proxy می‌نامند؛ چون ساخت Object پرهزینه را تا زمان نیاز به تأخیر می‌اندازد. Lazy Initialization و Caching از کاربردهای رایج Proxy هستند. <Cite refs={["turn0search0","turn0search1"]} />

Proxy چگونه از Lazy Loading استفاده می‌کند؟

Lazy Loading یعنی Object یا داده‌ای را فقط زمانی ایجاد یا بارگذاری کنیم که واقعاً به آن نیاز داریم.

بدون Proxy ممکن است کد زیر را داشته باشیم:


$image = new RealImage('products/laptop.jpg');

در این حالت، تصویر بلافاصله بارگذاری می‌شود؛ حتی اگر هیچ‌وقت از آن استفاده نکنیم.

با Proxy:


$image = new ImageProxy('products/laptop.jpg');

در این مرحله فقط Proxy ساخته می‌شود. تصویر واقعی در زمان اجرای display بارگذاری خواهد شد.

این روش در شرایط زیر مفید است:

  • تصاویر زیاد در یک صفحه وجود دارند.
  • Object ساخت سنگینی دارد.
  • اطلاعات از API خارجی دریافت می‌شود.
  • اتصال به سرویس هزینه‌بر است.
  • احتمال دارد Object ساخته شود ولی هرگز استفاده نشود.

البته Lazy Loading همیشه باعث بهبود عملکرد نمی‌شود. اگر Object در تمام حالت‌ها استفاده شود، ساخت Proxy ممکن است پیچیدگی اضافه ایجاد کند. بنابراین باید بر اساس هزینه ساخت Object و الگوی استفاده از آن تصمیم گرفت.

Protection Proxy؛ کنترل دسترسی با Proxy

یکی دیگر از کاربردهای مهم Proxy، کنترل دسترسی است.

فرض کنید یک سرویس برای دریافت گزارش مالی داریم و فقط کاربران مدیر باید بتوانند آن را اجرا کنند.

ابتدا Interface را تعریف می‌کنیم:


<?php



declare(strict_types=1);



interface ReportServiceInterface

{

    public function generate(): string;

}

کلاس اصلی گزارش:


<?php



declare(strict_types=1);



final class FinancialReportService implements ReportServiceInterface

{

    public function generate(): string

    {

        return 'Financial report generated.';

    }

}

حالا Proxy را می‌سازیم:


<?php



declare(strict_types=1);



final class ProtectedReportProxy implements ReportServiceInterface

{

    public function __construct(

        private readonly ReportServiceInterface $reportService,

        private readonly bool $isAdmin

    ) {

    }



    public function generate(): string

    {

        if (!$this->isAdmin) {

            throw new RuntimeException(

                'You are not allowed to access this report.'

            );

        }



        return $this->reportService->generate();

    }

}

استفاده:


<?php



$realService = new FinancialReportService();



$proxy = new ProtectedReportProxy(

    reportService: $realService,

    isAdmin: false

);



echo $proxy->generate();

در این حالت Exception رخ می‌دهد؛ چون کاربر مدیر نیست.

برای کاربر مدیر:


<?php



$proxy = new ProtectedReportProxy(

    reportService: $realService,

    isAdmin: true

);



echo $proxy->generate();

خروجی:


Financial report generated.

مزیت این روش آن است که منطق تولید گزارش در کلاس FinancialReportService باقی می‌ماند و منطق کنترل دسترسی در Proxy قرار می‌گیرد.

Caching Proxy؛ ذخیره نتیجه درخواست

فرض کنید یک سرویس برای دریافت قیمت دلار، وضعیت آب‌وهوا یا اطلاعات یک API خارجی داریم. ارسال درخواست در هر بار اجرا می‌تواند زمان‌بر یا پرهزینه باشد.

در این شرایط می‌توان از Caching Proxy استفاده کرد.

ابتدا Interface:


<?php



declare(strict_types=1);



interface ExchangeRateServiceInterface

{

    public function getRate(string $currency): float;

}

سرویس واقعی:


<?php



declare(strict_types=1);



final class RemoteExchangeRateService

    implements ExchangeRateServiceInterface

{

    public function getRate(string $currency): float

    {

        echo "Calling remote API..." . PHP_EOL;



        return match ($currency) {

            'USD' => 60000.0,

            'EUR' => 65000.0,

            default => 0.0,

        };

    }

}

حالا Proxy کش:


<?php



declare(strict_types=1);



final class CachedExchangeRateProxy

    implements ExchangeRateServiceInterface

{

    private array $cache = [];



    public function __construct(

        private readonly ExchangeRateServiceInterface $service

    ) {

    }



    public function getRate(string $currency): float

    {

        if (array_key_exists($currency, $this->cache)) {

            echo "Returning value from cache." . PHP_EOL;



            return $this->cache[$currency];

        }



        $rate = $this->service->getRate($currency);



        $this->cache[$currency] = $rate;



        return $rate;

    }

}

استفاده:


<?php



$service = new RemoteExchangeRateService();



$proxy = new CachedExchangeRateProxy($service);



echo $proxy->getRate('USD') . PHP_EOL;

echo $proxy->getRate('USD') . PHP_EOL;

خروجی:


Calling remote API...

60000

Returning value from cache.

60000

در این مثال، درخواست دوم به API ارسال نمی‌شود و نتیجه قبلی از Cache خوانده می‌شود.

در پروژه واقعی Laravel می‌توان به جای آرایه از موارد زیر استفاده کرد:

  • Cache::remember
  • Redis
  • Memcached
  • Database Cache
  • سیستم Cache اختصاصی

نمونه با Laravel:


<?php



namespace App\Services;



use Illuminate\Support\Facades\Cache;



final class CachedProductService

{

    public function __construct(

        private readonly ProductService $productService

    ) {

    }



    public function find(int $productId): array

    {

        return Cache::remember(

            "product:{$productId}",

            now()->addMinutes(10),

            fn (): array => $this->productService->find($productId)

        );

    }

}

در این نمونه، Proxy نتیجه سرویس اصلی را در Cache قرار می‌دهد و در درخواست‌های بعدی از مقدار ذخیره‌شده استفاده می‌کند.

Logging Proxy؛ ثبت زمان اجرای سرویس

گاهی لازم است بدون تغییر کلاس اصلی، مدت زمان اجرای یک عملیات را ثبت کنیم.


<?php



declare(strict_types=1);



interface UserServiceInterface

{

    public function find(int $id): array;

}

سرویس اصلی:


<?php



declare(strict_types=1);



final class UserService implements UserServiceInterface

{

    public function find(int $id): array

    {

        usleep(200000);



        return [

            'id' => $id,

            'name' => 'Saeed',

        ];

    }

}

Proxy ثبت Log:


<?php



declare(strict_types=1);



final class LoggingUserServiceProxy

    implements UserServiceInterface

{

    public function __construct(

        private readonly UserServiceInterface $service

    ) {

    }



    public function find(int $id): array

    {

        $startedAt = microtime(true);



        try {

            return $this->service->find($id);

        } finally {

            $duration = microtime(true) - $startedAt;



            error_log(

                "UserService::find executed in {$duration} seconds."

            );

        }

    }

}

استفاده:


<?php



$service = new UserService();



$proxy = new LoggingUserServiceProxy($service);



$user = $proxy->find(10);

در این حالت، سرویس اصلی هیچ اطلاعی از ثبت زمان اجرا ندارد و Proxy این مسئولیت را بر عهده گرفته است.

استفاده از Proxy در Laravel

در Laravel، Proxy می‌تواند برای سرویس‌های مختلف کاربرد داشته باشد:

  • سرویس‌های اتصال به API
  • سرویس‌های پرداخت
  • سرویس‌های دریافت نرخ ارز
  • سرویس‌های ارسال پیامک
  • سرویس‌های مدیریت فایل
  • سرویس‌های مربوط به n8n
  • سرویس‌های گزارش‌گیری
  • سرویس‌های دارای Cache
  • سرویس‌های نیازمند Authorization
  • سرویس‌های دارای Rate Limit

فرض کنید Interface زیر را داریم:


<?php



namespace App\Contracts;



interface SmsServiceInterface

{

    public function send(string $mobile, string $message): bool;

}

سرویس واقعی:


<?php



namespace App\Services;



use App\Contracts\SmsServiceInterface;



final class SmsService implements SmsServiceInterface

{

    public function send(string $mobile, string $message): bool

    {

        // ارسال واقعی پیامک به Provider



        return true;

    }

}

Proxy برای ثبت Log:


<?php



namespace App\Services;



use App\Contracts\SmsServiceInterface;

use Illuminate\Support\Facades\Log;



final class LoggingSmsProxy implements SmsServiceInterface

{

    public function __construct(

        private readonly SmsServiceInterface $service

    ) {

    }



    public function send(string $mobile, string $message): bool

    {

        Log::info('SMS sending started', [

            'mobile' => $mobile,

        ]);



        $result = $this->service->send($mobile, $message);



        Log::info('SMS sending finished', [

            'mobile' => $mobile,

            'success' => $result,

        ]);



        return $result;

    }

}

حالا در Service Container می‌توانیم Proxy را به عنوان پیاده‌سازی Interface ثبت کنیم:


<?php



use App\Contracts\SmsServiceInterface;

use App\Services\LoggingSmsProxy;

use App\Services\SmsService;



$this->app->bind(

    SmsServiceInterface::class,

    function ($app) {

        return new LoggingSmsProxy(

            $app->make(SmsService::class)

        );

    }

);

از این به بعد هر جا Interface را Inject کنیم، Proxy دریافت خواهد شد:


<?php



namespace App\Http\Controllers;



use App\Contracts\SmsServiceInterface;



final class SmsController

{

    public function __construct(

        private readonly SmsServiceInterface $smsService

    ) {

    }



    public function send()

    {

        $this->smsService->send(

            '09120000000',

            'Test message'

        );



        return response()->json([

            'message' => 'SMS sent.',

        ]);

    }

}

Controller از وجود Proxy مطلع نیست. این همان ویژگی مهم Proxy است: Client با Interface کار می‌کند، نه با کلاس پیاده‌سازی خاص.

تفاوت Proxy و Decorator

Proxy و Decorator شباهت زیادی دارند؛ هر دو می‌توانند یک Object را درون خود نگهداری کنند و همان Interface را پیاده‌سازی کنند.

اما هدف آن‌ها معمولاً متفاوت است.

Proxy

Proxy بیشتر برای موارد زیر استفاده می‌شود:

  • کنترل دسترسی
  • Lazy Loading
  • Remote Access
  • Cache
  • مدیریت چرخه عمر Object
  • محدودکردن دسترسی به سرویس

Decorator

Decorator معمولاً برای افزودن قابلیت‌های جدید به یک Object استفاده می‌شود؛ مانند:

  • افزودن قابلیت فشرده‌سازی
  • افزودن قابلیت رمزنگاری
  • افزودن قابلیت ارسال Notification
  • افزودن قابلیت گزارش‌گیری
  • ترکیب چند رفتار مختلف

در عمل، تفاوت Proxy و Decorator همیشه کاملاً قطعی نیست. مرز اصلی به هدف طراحی بستگی دارد. اگر تمرکز روی کنترل دسترسی یا جایگزینی Object اصلی باشد، Proxy انتخاب طبیعی‌تری است. اگر هدف افزودن قابلیت‌های قابل ترکیب باشد، Decorator مناسب‌تر خواهد بود.

تفاوت Proxy و Adapter

Adapter برای سازگارکردن دو Interface متفاوت استفاده می‌شود.

برای مثال، برنامه ما متد زیر را انتظار دارد:


interface PaymentGatewayInterface

{

    public function pay(int $amount): bool;

}

اما Provider خارجی متد دیگری دارد:


$provider->createTransaction($amount);

در اینجا Adapter لازم است تا Interface Provider را با Interface مورد انتظار برنامه سازگار کند.

اما Proxy معمولاً همان Interface Object اصلی را حفظ می‌کند و قبل یا بعد از اجرای آن عملیات دیگری انجام می‌دهد.

خلاصه:

الگوهدف اصلی
Proxyکنترل یا مدیریت دسترسی به Object
Decoratorافزودن رفتار جدید
Adapterسازگارکردن Interfaceهای متفاوت
Facadeارائه یک Interface ساده برای چند سیستم

انواع Proxy Pattern

Virtual Proxy

برای Lazy Loading و ایجاد Objectهای سنگین در زمان نیاز استفاده می‌شود.

Protection Proxy

برای کنترل دسترسی کاربران یا نقش‌ها کاربرد دارد.

Remote Proxy

برای دسترسی به سرویس یا Objectی که در سیستم دیگری قرار دارد استفاده می‌شود.

Caching Proxy

نتیجه درخواست‌ها را ذخیره می‌کند تا درخواست‌های تکراری سریع‌تر پاسخ داده شوند.

Logging Proxy

اطلاعات مربوط به اجرای متدها، خطاها و زمان پردازش را ثبت می‌کند.

Smart Proxy

علاوه بر واسط‌بودن، می‌تواند مسئولیت‌هایی مانند مدیریت منابع، شمارش استفاده یا بررسی وضعیت Object را نیز بر عهده بگیرد.

مزایای Proxy Pattern

۱. جداسازی مسئولیت‌ها

منطق اصلی سرویس از منطق کنترل دسترسی، Cache یا Log جدا می‌شود.

۲. عدم نیاز به تغییر Client

اگر Proxy همان Interface سرویس اصلی را پیاده‌سازی کند، Client معمولاً نیازی به تغییر ندارد.

۳. امکان Lazy Loading

Objectهای پرهزینه فقط هنگام نیاز ساخته می‌شوند.

۴. کنترل بهتر دسترسی

می‌توان قبل از اجرای عملیات، مجوزها را بررسی کرد.

۵. کاهش درخواست‌های تکراری

Caching Proxy می‌تواند درخواست‌های تکراری را مدیریت کند.

۶. تست‌پذیری بهتر

به کمک Interface و Dependency Injection می‌توان Proxy و سرویس اصلی را جداگانه تست کرد.

معایب Proxy Pattern

۱. افزایش تعداد کلاس‌ها

برای یک سرویس ساده ممکن است یک Interface، سرویس اصلی و Proxy ایجاد شود.

۲. پیچیدگی بیشتر

اگر Proxyها بدون نیاز واقعی استفاده شوند، معماری پروژه بیش از حد پیچیده می‌شود.

۳. احتمال پنهان‌شدن رفتار واقعی

ممکن است Developer نداند که Object دریافت‌شده Proxy است و عملیات اضافی مانند Cache یا Authorization انجام می‌شود.

۴. مدیریت دشوار Cache

در Caching Proxy باید به Expiration، Invalidating Cache و داده‌های قدیمی توجه شود.

۵. احتمال ایجاد چندلایه Proxy

اگر چند Proxy پشت سر هم قرار گیرند، Debug کردن جریان درخواست دشوارتر خواهد شد.

چه زمانی از Proxy استفاده کنیم؟

Proxy در شرایط زیر انتخاب مناسبی است:

  • سرویس اصلی هزینه پردازشی بالایی دارد.
  • می‌خواهیم ساخت Object را به تأخیر بیندازیم.
  • نیاز به کنترل دسترسی داریم.
  • می‌خواهیم نتیجه درخواست‌ها Cache شود.
  • سرویس خارجی یا Remote داریم.
  • نیاز به ثبت Log بدون تغییر کلاس اصلی داریم.
  • می‌خواهیم قابلیت Rate Limiting یا Retry را جدا کنیم.
  • کلاس اصلی متعلق به یک پکیج خارجی است و نمی‌خواهیم آن را تغییر دهیم.

چه زمانی از Proxy استفاده نکنیم؟

اگر سرویس بسیار ساده است و هیچ رفتار اضافی نیاز ندارد، استفاده از Proxy ممکن است فقط پیچیدگی ایجاد کند.

همچنین اگر هدف شما ترکیب چند رفتار مستقل و قابل تعویض است، Decorator یا Middleware ممکن است انتخاب مناسب‌تری باشد.

تست Proxy با PHPUnit

برای تست Caching Proxy می‌توان بررسی کرد که سرویس واقعی فقط یک بار اجرا شده است.

ابتدا یک Fake Service می‌سازیم:


<?php



declare(strict_types=1);



final class FakeRateService implements ExchangeRateServiceInterface

{

    public int $calls = 0;



    public function getRate(string $currency): float

    {

        $this->calls++;



        return 60000.0;

    }

}

تست:


<?php



declare(strict_types=1);



use PHPUnit\Framework\TestCase;



final class CachedExchangeRateProxyTest extends TestCase

{

    public function test_it_caches_repeated_requests(): void

    {

        $fakeService = new FakeRateService();



        $proxy = new CachedExchangeRateProxy($fakeService);



        $firstRate = $proxy->getRate('USD');

        $secondRate = $proxy->getRate('USD');



        self::assertSame(60000.0, $firstRate);

        self::assertSame(60000.0, $secondRate);

        self::assertSame(1, $fakeService->calls);

    }

}

در این تست، متد getRate دو بار روی Proxy اجرا می‌شود؛ اما سرویس واقعی فقط یک بار فراخوانی می‌شود.

برای Protection Proxy نیز می‌توان تست کرد که کاربر غیرمجاز نمی‌تواند به سرویس دسترسی داشته باشد:


<?php



declare(strict_types=1);



use PHPUnit\Framework\TestCase;



final class ProtectedReportProxyTest extends TestCase

{

    public function test_non_admin_cannot_generate_report(): void

    {

        $service = new FinancialReportService();



        $proxy = new ProtectedReportProxy(

            reportService: $service,

            isAdmin: false

        );



        $this->expectException(RuntimeException::class);



        $proxy->generate();

    }

}

اشتباهات رایج هنگام استفاده از Proxy

اشتباه اول: قرار دادن تمام منطق برنامه در Proxy

Proxy نباید به یک کلاس بزرگ و پیچیده تبدیل شود. بهتر است هر Proxy یک مسئولیت مشخص داشته باشد.

اشتباه دوم: نداشتن Interface مشترک

اگر Proxy و Real Subject Interface مشترکی نداشته باشند، Client ممکن است به کلاس خاصی وابسته شود.

اشتباه سوم: Cache بدون Expiration

ذخیره دائمی داده‌ها می‌تواند باعث نمایش اطلاعات قدیمی شود. Cache باید زمان انقضا و روش پاک‌سازی داشته باشد.

اشتباه چهارم: کنترل دسترسی فقط در Proxy

Proxy می‌تواند یک لایه کنترل دسترسی باشد، اما برای عملیات حساس باید مجوزها در سطح مناسب سیستم نیز بررسی شوند.

اشتباه پنجم: ایجاد Proxy برای همه کلاس‌ها

Proxy یک ابزار طراحی است، نه چیزی که باید در تمام سرویس‌ها استفاده شود. فقط زمانی از آن استفاده کنید که نیاز مشخصی وجود داشته باشد.

اشتباه ششم: پنهان‌کردن خطاها

Proxy نباید خطاهای سرویس اصلی را بدون دلیل مخفی کند. در صورت نیاز باید خطا ثبت، تبدیل یا دوباره پرتاب شود.

نمونه کامل Proxy Pattern در PHP

کد زیر یک نمونه ساده و کامل از Proxy برای Cache را نشان می‌دهد:


<?php



declare(strict_types=1);



interface ProductServiceInterface

{

    public function find(int $id): array;

}



final class ProductService implements ProductServiceInterface

{

    public function find(int $id): array

    {

        echo "Loading product from database." . PHP_EOL;



        return [

            'id' => $id,

            'title' => 'Laptop',

            'price' => 50000000,

        ];

    }

}



final class ProductCacheProxy implements ProductServiceInterface

{

    private array $cache = [];



    public function __construct(

        private readonly ProductServiceInterface $service

    ) {

    }



    public function find(int $id): array

    {

        if (isset($this->cache[$id])) {

            echo "Loading product from cache." . PHP_EOL;



            return $this->cache[$id];

        }



        $product = $this->service->find($id);



        $this->cache[$id] = $product;



        return $product;

    }

}



function showProduct(ProductServiceInterface $service): void

{

    $product = $service->find(1);



    echo $product['title'] . PHP_EOL;

}



$realService = new ProductService();



$proxy = new ProductCacheProxy($realService);



showProduct($proxy);

showProduct($proxy);

خروجی:


Loading product from database.

Laptop

Loading product from cache.

Laptop

تابع showProduct نمی‌داند که Object دریافتی یک سرویس واقعی است یا Proxy. این موضوع باعث می‌شود بتوانیم رفتار سرویس را بدون تغییر کد Client مدیریت کنیم.

جمع‌بندی

Proxy Pattern در PHP یک الگوی ساختاری است که یک Object واسط را بین Client و Object اصلی قرار می‌دهد.

Proxy می‌تواند قبل یا بعد از اجرای عملیات اصلی، کارهایی مانند موارد زیر را انجام دهد:

  • کنترل دسترسی
  • Cache
  • Lazy Loading
  • ثبت Log
  • مدیریت درخواست‌های Remote
  • کنترل منابع
  • اعتبارسنجی
  • Rate Limiting

مهم‌ترین ویژگی Proxy این است که معمولاً همان Interface مربوط به Real Subject را پیاده‌سازی می‌کند. بنابراین Client می‌تواند بدون اطلاع از جزئیات، با Proxy کار کند.

در پروژه‌های PHP و Laravel، این الگو برای سرویس‌های API، پرداخت، پیامک، گزارش‌گیری، Storage، مدیریت محصولات و سرویس‌های پرهزینه کاربرد زیادی دارد. با این حال، استفاده از Proxy باید بر اساس نیاز واقعی انجام شود؛ زیرا در پروژه‌های کوچک ممکن است پیچیدگی غیرضروری ایجاد کند.

سوالات متداول

Proxy Pattern در PHP چیست؟

Proxy Pattern یک الگوی ساختاری است که یک Object جایگزین را بین Client و Object اصلی قرار می‌دهد تا بتواند کنترل دسترسی، Cache، Lazy Loading یا عملیات دیگری را مدیریت کند.

تفاوت Proxy و Decorator چیست؟

Proxy بیشتر برای کنترل دسترسی، مدیریت چرخه عمر یا جایگزینی Object اصلی استفاده می‌شود؛ در حالی که Decorator معمولاً برای افزودن رفتارهای جدید و قابل ترکیب به یک Object کاربرد دارد.

آیا Proxy همیشه باعث افزایش سرعت می‌شود؟

خیر. Proxy در شرایطی مانند Cache یا Lazy Loading می‌تواند عملکرد را بهتر کند، اما خود Proxy نیز پیچیدگی و یک لایه اضافی به سیستم اضافه می‌کند.

آیا Proxy در Laravel کاربرد دارد؟

بله. می‌توان Proxy را برای سرویس‌های API، Cache، پیامک، پرداخت، گزارش‌گیری، کنترل دسترسی و سرویس‌های خارجی در Laravel استفاده کرد.

Virtual Proxy چیست؟

Virtual Proxy نوعی Proxy است که ساخت Object سنگین را تا زمانی که واقعاً به آن نیاز باشد به تأخیر می‌اندازد.

Protection Proxy چه کاربردی دارد؟

Protection Proxy برای بررسی مجوز دسترسی قبل از اجرای عملیات اصلی استفاده می‌شود؛ برای مثال محدودکردن دسترسی به گزارش مالی یا اطلاعات محرمانه.

آیا Proxy باید همان Interface سرویس اصلی را داشته باشد؟

در طراحی معمول Proxy، بله. داشتن Interface مشترک باعث می‌شود Client بتواند بدون تغییر با Proxy یا Real Subject کار کند. <Cite refs={["turn0search0","turn0search1"]} />

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه