زمان تخمینی مطالعه: 10 دقیقه

الگوی Proxy یک الگوی طراحی ساختاری است که به برنامه نویس امکان می‌دهد یک جایگزین یا مکان نگهدار(Placeholder) برای یک شی دیگر تهیه کنید. یک پروکسی دسترسی به شی اصلی را کنترل کرده و به شما امکان می‌دهد قبل یا بعد از ارسال درخواست به شی اصلی، کاری را انجام دهید.

Proxy design pattern

بیان مسئله: خوب اطلا چرا باید دسترسی به یک شی را کنترل کنیم؟ اجازه دهید با مثالی شرح دهیم: شما یک شی عظیم دارید که مقدار زیادی از منابع سیستم را مصرف می‌کند. در طول برنامه خود شما هر از گاهی به آن نیاز دارید، اما نه همیشه.

Problem solved by Proxy pattern
پرس و جوهای پایگاه داده می‌تواند واقعا کند باشد.

می‌توانید مقداردهی اولیه تنبل(lazy) را پیاده‌سازی کنید: شی را فقط زمانی ایجاد کنید که واقعاً مورد نیاز است. همه کلاینت‌های شی باید مقداری کد اولیه معوق را اجرا کنند. متأسفانه، این احتمالاً باعث تکرار کدهای زیادی می‌شود. در یک دنیای ایده آل، ما می‌خواهیم این کد را مستقیماً در کلاس شیء خود قرار دهیم، اما این همیشه ممکن نیست. به عنوان مثال، کلاس ممکن است بخشی از یک کتابخانه شخص ثالث بسته باشد.

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

Solution with the Proxy pattern
پروکسی خود را به عنوان یک شی پایگاه داده پنهان می‌کند. این روش می‌تواند بدون اینکه مشتری یا شی پایگاه داده واقعی بداند، مقداردهی اولیه و کش نتایج را مدیریت کند.

اما این تکنیک چه سودی خواهد داشت؟ اگر لازم است چیزی را قبل یا بعد از منطق اولیه کلاس اجرا کنید، پروکسی به شما اجازه می‌دهد این کار را بدون تغییر آن کلاس انجام دهید. از آنجایی که پروکسی همان اینترفیس کلاس اصلی را پیاده‌سازی می‌کند، می‌توان آن را به هر کلاینتی که انتظار یک شیء سرویس واقعی را دارد، ارسال کرد.

نمونه قابل قیاس در دنیای واقعی

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

A credit card is a proxy for a bundle of cash
کارت‌های اعتباری را می‌توان همانند پول نقد برای پرداخت استفاده کرد.


ساختار الگوی Proxy

در این بخش به بررسی ساختار الگوی پروکسی(Proxy) می‌پردازیم و پیاده‌سازی آن را به صورت UML خواهیم دید. 

شبه کد(Pseudocode)

این مثال نشان می‌دهد که چگونه الگوی Proxy می‌تواند به معرفی تنظیم اولیه تنبل(lazy initialization) و ذخیره‌سازی پنهان به یک کتابخانه یکپارچه YouTube شخص ثالث کمک کند.

Structure of the Proxy pattern example
کش کردن نتایج یک سرویس با پروکسی

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

// The interface of a remote service.
interface ThirdPartyYouTubeLib is
    method listVideos()
    method getVideoInfo(id)
    method downloadVideo(id)

// The concrete implementation of a service connector. Methods
// of this class can request information from YouTube. The speed
// of the request depends on a user's internet connection as
// well as YouTube's. The application will slow down if a lot of
// requests are fired at the same time, even if they all request
// the same information.
class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib is
    method listVideos() is
        // Send an API request to YouTube.

    method getVideoInfo(id) is
        // Get metadata about some video.

    method downloadVideo(id) is
        // Download a video file from YouTube.

// To save some bandwidth, we can cache request results and keep
// them for some time. But it may be impossible to put such code
// directly into the service class. For example, it could have
// been provided as part of a third party library and/or defined
// as `final`. That's why we put the caching code into a new
// proxy class which implements the same interface as the
// service class. It delegates to the service object only when
// the real requests have to be sent.
class CachedYouTubeClass implements ThirdPartyYouTubeLib is
    private field service: ThirdPartyYouTubeLib
    private field listCache, videoCache
    field needReset

    constructor CachedYouTubeClass(service: ThirdPartyYouTubeLib) is
        this.service = service

    method listVideos() is
        if (listCache == null || needReset)
            listCache = service.listVideos()
        return listCache

    method getVideoInfo(id) is
        if (videoCache == null || needReset)
            videoCache = service.getVideoInfo(id)
        return videoCache

    method downloadVideo(id) is
        if (!downloadExists(id) || needReset)
            service.downloadVideo(id)

// The GUI class, which used to work directly with a service
// object, stays unchanged as long as it works with the service
// object through an interface. We can safely pass a proxy
// object instead of a real service object since they both
// implement the same interface.
class YouTubeManager is
    protected field service: ThirdPartyYouTubeLib

    constructor YouTubeManager(service: ThirdPartyYouTubeLib) is
        this.service = service

    method renderVideoPage(id) is
        info = service.getVideoInfo(id)
        // Render the video page.

    method renderListPanel() is
        list = service.listVideos()
        // Render the list of video thumbnails.

    method reactOnUserInput() is
        renderVideoPage()
        renderListPanel()

// The application can configure proxies on the fly.
class Application is
    method init() is
        aYouTubeService = new ThirdPartyYouTubeClass()
        aYouTubeProxy = new CachedYouTubeClass(aYouTubeService)
        manager = new YouTubeManager(aYouTubeProxy)
        manager.reactOnUserInput()

قابلیت‌ها و کاربرد‌ها

راه‌های زیادی برای استفاده از الگوی Proxy وجود دارد. در این مجال بیایید به محبوب‌ترین کاربردهای آن بپردازیم:

پروکسی همچنین می‌تواند ردیابی کند که آیا مشتری شیء سرویس را تغییر داده است یا خیر. سپس اشیاء بدون تغییر ممکن است توسط مشتریان دیگر مورد استفاده مجدد قرار گیرند.

نحوه پیاده‌سازی

  1. اگر هیچ اینترفیس سرویسی از قبل موجود نیست، یک اینترفیس ایجاد کنید تا اشیاء پراکسی و سرویس قابل تعویض باشند. استخراج اینترفیس از کلاس سرویس همیشه امکان پذیر نیست، زیرا برای استفاده از آن اینترفیس ، باید همه مشتریان سرویس را تغییر دهید. پلن B این است که پروکسی را به یک زیر کلاس از کلاس سرویس تبدیل کند و به این ترتیب اینترفیس سرویس را به ارث می‌برد.
  2. کلاس پروکسی را ایجاد کنید. باید یک فیلد برای ذخیره ارجاع به سرویس داشته باشد. معمولاً پروکسی‌ها کل چرخه عمر سرویس‌های خود را ایجاد و مدیریت می‌کنند. در موارد نادر، یک سرویس از طریق یک سازنده توسط مشتری به پروکسی ارسال می‌شود.
  3. متدهای پروکسی را با توجه به اهداف آنها پیاده‌سازی کنید. در بیشتر موارد، پس از انجام برخی کارها، پروکسی باید کار را به شیء سرویس واگذار کند.
  4. یک متد ایجاد را در نظر بگیرید که تصمیم می‌گیرد مشتری یک پروکسی یا یک سرویس واقعی دریافت کند. این می‌تواند یک روش استاتیک ساده در کلاس پروکسی یا یک روش Factory تمام عیار باشد.
  5. پیاده‌سازی lazy initialization را برای شی سرویس در نظر بگیرید.

مزایا و معایب الگوی Decorator

این الگوی طراحی دارای مزایا و معایبی به شرح زیر است:

– مزایا

– معایب

ارتباط با الگوهای دیگر

نمونه کد الگوی Proxy

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *