الگوی طراحی نایب (Proxy) در زبان C#

توضیح دوات: برای راهنمایی و اطلاعات بیشتر در مورد نمودار کلاس به این مقاله مراجعه کنید.

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

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

می‌توانید کد نمونه این پست را در GitHub پیدا کنید.

درک مسئله

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

می‌توانیم از lazy initialization استفاده کنیم تا شیء فقط زمانی ایجاد شود که به آن نیاز داریم. در این صورت، تمام مشتریان شیء باید کدی برای راه‌اندازی و مقداردهی اولیه آن اجرا کنند. این کار احتمالاً منجر به تکرار کد زیادی می‌شود که خوب نیست.

در دنیای ایده‌آل، ما می‌خواهیم این کد را مستقیماً در کلاس شیء قرار دهیم، اما گاهی اوقات این کار ممکن نیست. مثلاً ممکن است کلاس بخشی از یک بسته نرم‌افزاری بسته و متعلق به شخص ثالث باشد.

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

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

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

در این پیاده‌سازی پایه، الگوی Proxy چهار شرکت‌کننده دارد:

  1. Service Interface: رابط سرویس، رابط شیء سرویس را اعلام می‌کند. Proxy باید از این رابط پیروی کند تا بتواند خود را به‌عنوان یک شیء سرویس معرفی کند.
  2. Service: سرویس، کلاسی است که برخی از منطق‌های کسب‌وکار مفید را ارائه می‌دهد.
  3. Proxy: کلاس Proxy یک فیلد ارجاعی دارد که به یک شیء سرویس اشاره می‌کند. پس از اتمام پردازش Proxy (مثلاً lazy initialization، ثبت گزارش، کنترل دسترسی، کش کردن و غیره)، درخواست را به شیء سرویس انتقال می‌دهد. معمولاً Proxy چرخه کامل حیات شیء سرویس خود را مدیریت می‌کند.
  4. Client: مشتری باید با سرویس‌ها و Proxy‌ها از طریق همان رابط کار کند. به این ترتیب، می‌توان یک Proxy را به هر کدی که انتظار یک شیء سرویس را دارد، پاس داد.

نمودار کلاس الگوی طراحی Proxy

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

ابتدا یک رابط ایجاد خواهیم کرد که به‌عنوان Service Interface ما عمل کند. این رابط یک متد RunCommand ارائه می‌دهد که یک پارامتر رشته‌ای، یعنی command، می‌گیرد. این متد برای اجرای یک فرمان در سیستم‌عامل طراحی شده است:

				
					namespace Proxy;  

public interface ICommandExecutor  
{  
    public void RunCommand(string command);  
}

				
			

در مرحله بعد، شرکت‌کننده Service خود را ایجاد خواهیم کرد. این کلاس یک پیاده‌سازی مشخص از متد RunCommand که در بالا تعریف شد، ارائه می‌دهد:

				
					public class CommandExecutor : ICommandExecutor
{
    public void RunCommand(string command)
    {
        // some heavy implementation
        var processInfo = new ProcessStartInfo()
        {
            FileName = "cmd.exe",
            WorkingDirectory = Environment.CurrentDirectory,
                Arguments = "/C " + command
        };

        using var process = Process.Start(processInfo);
        process.WaitForExit();
        Console.WriteLine("'" + command + "' command executed.");
    }
}

				
			

این متد یک نمونه جدید از ProcessStartInfo ایجاد می‌کند که حاوی اطلاعات لازم برای راه‌اندازی یک فرآیند جدید است. ویژگی FileName روی cmd.exe تنظیم می‌شود که مفسر خط فرمان ویندوز است. ویژگی WorkingDirectory روی پوشه فعلی تنظیم می‌شود، جایی که فرمان توسط فرآیند جدید اجرا خواهد شد. ویژگی Arguments به مقدار "/C " + command تنظیم می‌شود، به این معنا که cmd.exe باید فرمان مشخص‌شده در پارامتر command را اجرا کند.

سپس متد فرآیند را با استفاده از کلاس Process آغاز کرده و شیء بازگشتی Process را به یک متغیر با نام process اختصاص می‌دهد. دستور using تضمین می‌کند که شیء process پس از تکمیل کد موجود در دستور، به‌درستی آزاد می‌شود.

سپس متد از متد WaitForExit() استفاده می‌کند تا منتظر خروج فرآیند بماند، که باعث مسدود شدن رشته جاری تا زمان خروج فرآیند می‌شود. در نهایت، متد پیامی به کنسول ارسال می‌کند که نشان می‌دهد فرمان با موفقیت اجرا شده است.

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

				
					namespace Proxy;

public class CommandExecutorProxy : ICommandExecutor
{
    private bool _isAdmin = false;
    private ICommandExecutor _executor;

    public CommandExecutorProxy(string username, string password)
    {
        if ("admin".Equals(username) && "1234".Equals(password))
            _isAdmin = true;

        _executor = new CommandExecutor();
    }

    public void RunCommand(string command)
    {
        if(_isAdmin)
            _executor.RunCommand(command);
        else
        {
            if (command.Trim().StartsWith("rm"))
                throw new Exception("rm command is not allowed for non-admin users");
            else
                _executor.RunCommand(command);
        }
    }
}

				
			

ابتدا کلاس دو فیلد تعریف می‌کند: _isAdmin که یک نشانگر بولی است و نشان می‌دهد کاربر دارای حقوق مدیر سیستم است یا خیر، و _executor که یک نمونه از شرکت‌کننده Service ما است. این همان شیء واقعی است که فرمان را اجرا می‌کند.

کلاس دارای یک سازنده است که یک فرآیند ورود بسیار ساده را انجام می‌دهد. اگر نام کاربری و رمز عبور با اطلاعات مدیر مطابقت داشته باشند، مقدار _isAdmin را به true تنظیم می‌کند. اما، اگرچه ممکن است برای تازه‌کاران به‌عنوان یک سیستم ورود ساده به نظر برسد، در واقع یک دژ امنیتی شگفت‌انگیز است که در برابر هر نوع نفوذ غیرمجاز غیرقابل نفوذ است!

این کلاس همچنین رابط ICommandExecutor را پیاده‌سازی می‌کند، بنابراین نیاز دارد یک پیاده‌سازی مشخص از متد RunCommand ارائه دهد. این متد ابتدا پرچم _isAdmin را بررسی می‌کند و اگر کاربر مدیر باشد، فرمان را به نمونه _command ارسال می‌کند. اما اگر کاربر مدیر نباشد، این متد با مهارتی فوق‌العاده تمامی فرمان‌های مخربی را که ممکن است تلاش کنند به حریم امنیتی آن تجاوز کنند، با استفاده از یک سیستم قضاوت فوق‌العاده قاطع جدا می‌کند. اگر فرمان با rm شروع شود، یک استثنا پرتاب می‌کند.

گام نهایی فراخوانی کلاس اصلی ما است:

				
					ICommandExecutor executor = new CommandExecutorProxy("admin", "wrong_pass");
try
{
    executor.RunCommand("dir");
    executor.RunCommand("rm -rf abc.pdf");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

				
			

این متد یک executor جدید ایجاد کرده و یک نام کاربری و رمز عبور ارائه می‌دهد. سپس تلاش می‌کند دو فرمان را اجرا کند. اگر کد بالا را اجرا کنیم، خروجی زیر را دریافت خواهیم کرد:

خروجی الگوی طراحی Proxy

همان‌طور که می‌بینید، کاربر مدیر نبود و Proxy اجرای فرمان rm را مسدود کرد.

انواع Proxy

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

Remote Proxy

یک Proxy شبکه که به مشتریان اجازه می‌دهد به منابع از راه دور دسترسی پیدا کنند، گویی این منابع محلی هستند، یک “Remote Proxy” نامیده می‌شود. به عبارت دیگر، یک Remote Proxy واسطه‌ای بین مشتری و سرور از راه دور است. این Proxy درخواست‌های مشتری را به سرور ارسال کرده و پاسخ‌های سرور را به مشتری بازمی‌گرداند.

Remote Proxy‌ها اغلب برای بهبود سرعت شبکه استفاده می‌شوند، با ذخیره منابعی که زیاد درخواست می‌شوند و کاهش میزان ترافیکی که باید از طریق شبکه منتقل شود. همچنین می‌توان از آنها برای دسترسی به منابع موجود در یک شبکه دیگر یا برای کنترل دسترسی به منابع با اعمال قوانین دسترسی و مسدود کردن ترافیک ناخواسته استفاده کرد.

Virtual Proxy

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

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

وقتی مشتری شیء را درخواست می‌کند، Virtual Proxy شیء واقعی را ایجاد یا شروع کرده و درخواست را به آن ارسال می‌کند. سپس درخواست‌ها می‌توانند مستقیماً به شیء واقعی ارسال شوند و Virtual Proxy را کنار بگذارند.

علاوه بر به‌تعویق انداختن ایجاد یا مقداردهی اولیه اشیاء، Virtual Proxy‌ها می‌توانند ویژگی‌های اضافی مانند کش کردن، کنترل دسترسی یا ثبت گزارش ارائه دهند.

یک مثال کوتاه از Virtual Proxy می‌تواند به صورت زیر باشد:

				
					// Define the interface that the expensive object will implement
public interface IExpensiveObject
{
    void DoSomethingExpensive();
}

// Define the class that implements the expensive object
public class ExpensiveObject : IExpensiveObject
{
    public ExpensiveObject()
    {
        // Simulate expensive initialization
        Thread.Sleep(5000);
        Console.WriteLine("Expensive object initialized.");
    }

    public void DoSomethingExpensive()
    {
        Console.WriteLine("Expensive object doing something expensive...");
    }
}

// Define the virtual proxy class that the client will use to access the expensive object
public class VirtualProxy : IExpensiveObject
{
    private IExpensiveObject _expensiveObject;

    public void DoSomethingExpensive()
    {
        // Delay the creation of the expensive object until it is actually needed
        if (_expensiveObject == null)
        {
            Console.WriteLine("Creating expensive object...");
            _expensiveObject = new ExpensiveObject();
        }

        // Forward the request to the expensive object
        _expensiveObject.DoSomethingExpensive();
    }
}

// Example usage
var virtualProxy = new VirtualProxy();
Console.WriteLine("Virtual proxy created.");
virtualProxy.DoSomethingExpensive();
virtualProxy.DoSomethingExpensive();
				
			

ما کلاسی به نام ExpensiveObject و یک رابط به نام IExpensiveObject تعریف می‌کنیم که شیء گران‌قیمت آن را پیاده‌سازی می‌کند. برای اینکه مشتری بتواند به شیء گران‌قیمت دسترسی پیدا کند، کلاس VirtualProxy را نیز تعریف می‌کنیم.

کلاس VirtualProxy از ایجاد شیء گران‌قیمت جلوگیری می‌کند تا زمانی که مشتری واقعاً به آن نیاز داشته باشد. وقتی مشتری شیء را درخواست می‌کند، VirtualProxy شیء گران‌قیمت را ایجاد کرده و درخواست را به آن ارسال می‌کند. دفعه بعد که شیء درخواست می‌شود، درخواست‌ها می‌توانند مستقیماً به شیء گران‌قیمت ارسال شوند.

Protection Proxy

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

هدف Protection Proxy این است که به یک شیء سطحی از امنیت و حفاظت بدهد تا فقط مشتریان مجاز بتوانند به آن دسترسی داشته باشند. Protection Proxy می‌تواند برای محدود کردن دسترسی به برخی از متدها یا ویژگی‌های شیء واقعی استفاده شود یا ویژگی‌های اضافی مانند ثبت گزارش یا ممیزی اضافه کند.

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

پیاده‌سازی CommandExecutorProxy که در بالا توضیح داده شد، یک مثال از Protection Proxy است.

Logging Proxy

یک Logging Proxy یک الگوی طراحی است که در آن یک شیء برای رهگیری و ثبت تماس‌های متد و سایر رویدادها به نمایندگی از یک شیء دیگر استفاده می‌شود. Logging Proxy به‌عنوان واسطه‌ای بین مشتری و شیء واقعی عمل می‌کند. این پروکسی اطلاعات مربوط به تماس‌های متد و رویدادها را ثبت کرده و در یک فایل ثبت یا مکان دیگری ذخیره می‌کند.

هدف Logging Proxy این است که به شما امکان دهد رفتار یک شیء را بدون تغییر خود شیء یا کدی که از آن استفاده می‌کند، نظارت و تحلیل کنید. این امر می‌تواند به‌ویژه برای برنامه‌هایی که به اطلاعات دقیق برای ممیزی یا اشکال‌زدایی نیاز دارند، یا برنامه‌هایی که باید الگوهای استفاده یا معیارهای عملکرد را پیگیری کنند، مفید باشد.

وقتی مشتری درخواست دسترسی به شیء را می‌دهد، Logging Proxy تماس متد یا رویداد را رهگیری کرده و اطلاعاتی مانند نام متد، پارامترها، مقدار بازگشتی و زمان رویداد را ثبت می‌کند. سپس این اطلاعات می‌توانند در یک فایل ثبت یا مکان دیگری ذخیره شوند و بعداً استفاده یا تحلیل شوند.

Logging Proxy‌ها می‌توانند علاوه بر ثبت تماس‌های متد و رویدادها، برای کش کردن، بررسی‌های امنیتی و کنترل دسترسی نیز استفاده شوند.

یک مثال کوچک از Logging Proxy به صورت زیر ارائه می‌شود:

				
					// Define the interface that the real object will implement
public interface IRealObject
{
    void DoSomething();
}

// Define the class that implements the real object
public class RealObject : IRealObject
{
    public void DoSomething()
    {
        Console.WriteLine("RealObject: DoSomething() called.");
    }
}

// Define the logging proxy class that will log method calls on the real object
public class LoggingProxy : IRealObject
{
    private readonly RealObject _realObject;

    public LoggingProxy()
    {
        _realObject = new RealObject();
    }

    public void DoSomething()
    {
        Console.WriteLine("LoggingProxy: DoSomething() called.");
        _realObject.DoSomething();
        Console.WriteLine("LoggingProxy: DoSomething() finished.");
    }
}

// Example usage
var loggingProxy = new LoggingProxy();
loggingProxy.DoSomething();
				
			

در این مثال، ما یک رابط به نام IRealObject تعریف می‌کنیم که شیء واقعی آن را پیاده‌سازی می‌کند و یک کلاس به نام RealObject که شیء واقعی را پیاده‌سازی می‌کند. همچنین کلاس LoggingProxy را تعریف می‌کنیم که تماس‌های متدها بر روی شیء واقعی را رهگیری و ثبت می‌کند.

کلاس LoggingProxy در سازنده خود یک نمونه از شیء واقعی ایجاد می‌کند و تماس‌های متدها را به شیء واقعی ارسال می‌کند، در حالی که اطلاعات را قبل و بعد از تماس متد ثبت می‌کند.

وقتی می‌خواهیم شیء واقعی را فراخوانی کنیم، یک LoggingProxy جدید ایجاد می‌کنیم. هنگامی که متد DoSomething() را بر روی Logging Proxy فراخوانی می‌کنیم، این فراخوانی را رهگیری کرده، اطلاعات مربوط به آن را ثبت می‌کند، تماس را به شیء واقعی ارسال کرده و اطلاعات مربوط به پایان تماس متد را ثبت می‌کند.

Caching Proxy

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

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

در اینجا یک مثال ساده از یک Caching Proxy در C# آورده شده است:

				
					// Define the interface that the real object will implement
public interface IExpensiveObject
{
    string GetResult();
}

// Define the class that implements the real object
public class ExpensiveObject : IExpensiveObject
{
    public string GetResult()
    {
        // Simulate an expensive operation
        Thread.Sleep(500);
        return "Result from ExpensiveObject";
    }
}

// Define the caching proxy class that will cache the results of calls to the real object
public class CachingProxy : IExpensiveObject
{
    private readonly IExpensiveObject _realObject;
    private string _cachedResult;

    public CachingProxy(IExpensiveObject realObject)
    {
        _realObject = realObject;
    }

    public string GetResult()
    {
        if (_cachedResult == null)
        {
            _cachedResult = _realObject.GetResult();
        }

        return _cachedResult;
    }
}

// Example usage
var expensiveObject = new ExpensiveObject();
var cachingProxy = new CachingProxy(expensiveObject);

// First call will be slow, as the result is not cached
Console.WriteLine(cachingProxy.GetResult());

// Subsequent calls will be fast, as the result is cached
Console.WriteLine(cachingProxy.GetResult());
				
			

در این مثال، شیء واقعی که یک عملیات گران‌قیمت انجام می‌دهد (در اینجا، شبیه‌سازی عملیاتی با تأخیر ۵۰۰ میلی‌ثانیه‌ای)، توسط کلاس ExpensiveObject نمایش داده شده است. کلاس CachingProxy به‌عنوان یک پوشش کش اطراف شیء واقعی عمل می‌کند. این کلاس تماس‌ها به متد GetResult() را رهگیری کرده و بررسی می‌کند که آیا نتیجه قبلاً در کش ذخیره شده است یا خیر. اگر نتیجه در کش ذخیره نشده باشد، متد بر روی شیء واقعی فراخوانی می‌شود و نتیجه قبل از بازگرداندن به مشتری در کش ذخیره می‌شود. اگر نتیجه قبلاً در کش باشد، مقدار کش‌شده بازگردانده می‌شود و نیازی به اجرای متد شیء واقعی نیست.

استفاده از Caching Proxy باعث می‌شود که تماس‌های بعدی به GetResult() بسیار سریع‌تر انجام شوند زیرا نتیجه قبلاً در کش ذخیره شده و می‌توان فوراً آن را بازگرداند.

الگوی طراحی Proxy در محیط میکروسرویس

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

در این شرایط، سرویس پروکسی می‌تواند در موارد زیر کمک کند:

  • توزیع بار (Load Balancing): پروکسی می‌تواند درخواست‌ها را به چندین نسخه از یک سرویس پشتیبان ارسال کند تا بار به طور مساوی توزیع شود. این کمک می‌کند که هیچ نمونه‌ای از سرویس به‌تنهایی تحت بار زیاد قرار نگیرد و کل سیستم پاسخگوتر و در دسترس‌تر باشد. پروکسی می‌تواند از الگوریتم‌های مختلف توزیع بار، مانند round-robin، کمترین تعداد اتصال (Least Connections)، یا IP hash استفاده کند تا تصمیم بگیرد کدام نمونه سرویس پشتیبان باید هر درخواست را مدیریت کند.
  • کشینگ (Caching): پروکسی می‌تواند پاسخ‌های سرویس‌های پشتیبان را ذخیره کند، که این امکان را می‌دهد درخواست‌های بعدی سریع‌تر پردازش شوند. این ویژگی به‌ویژه برای درخواست‌هایی که پردازش آنها زمان‌بر است یا محتوای ثابت یا تقریباً ثابتی بازمی‌گردانند، مفید است. پروکسی می‌تواند از استراتژی‌های مختلف کش، مانند انقضای زمانی یا حذف LRU، برای مدیریت کش استفاده کند.
  • امنیت (Security): پروکسی می‌تواند دسترسی کاربران به سرویس‌های پشتیبان را با اعمال سیاست‌های احراز هویت و مجوز کنترل کند. این کار کمک می‌کند که از دسترسی غیرمجاز یا سوءاستفاده از سیستم جلوگیری شود. پروکسی می‌تواند از پروتکل‌های امنیتی مختلفی مانند OAuth2 یا JWT برای مدیریت احراز هویت و مجوز استفاده کند.
  • مانیتورینگ (Monitoring): پروکسی می‌تواند متریک‌ها و گزارش‌هایی از سرویس‌های پشتیبان دریافت کند که نشان‌دهنده عملکرد و سلامت آنها هستند. این ویژگی می‌تواند به شناسایی مشکلات و گلوگاه‌ها در سیستم کمک کرده و عملکرد و بهره‌وری منابع را بهبود بخشد. پروکسی می‌تواند از ابزارهای مانیتورینگ مختلف مانند Prometheus یا Grafana برای جمع‌آوری و نمایش متریک‌ها استفاده کند.

معرفی Circuit Breaker

Circuit Breaker یک روش برای مدیریت خطاها و افزایش پایداری سیستم در سیستم‌های توزیع‌شده است. در یک معماری میکروسرویس، می‌توان یک Circuit Breaker را با الگوی طراحی Proxy پیاده‌سازی کرد تا نحوه تعامل مشتریان و سرویس‌های پشتیبان را کنترل کند.

Circuit Breaker با بررسی وضعیت سرویس از راه دور کار می‌کند و اگر سرویس خراب باشد یا پاسخ ندهد، مدار را قطع می‌کند (یعنی از ارسال درخواست‌ها به سرویس جلوگیری می‌کند). این کار از افزایش بار سرویس با درخواست‌های بیشتر جلوگیری می‌کند و می‌تواند به متوقف کردن انتشار خطاها کمک کند.

مزایا و معایب الگوی طراحی Proxy

مزایا

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

معایب

  • پاسخ از سرویس ممکن است با تأخیر مواجه شود.
  • کد ممکن است پیچیده‌تر شود زیرا نیاز به معرفی کلاس‌های جدید زیادی داریم.

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

  • شیئی که پوشش داده می‌شود، از Adapter یک رابط متفاوت، از Proxy همان رابط و از Decorator یک رابط بهبودیافته دریافت می‌کند.
  • هم Facade و هم Proxy یک موجودیت پیچیده را بافر می‌کنند و آن را خودشان راه‌اندازی می‌کنند. برخلاف Facade، Proxy و شیء سرویس آن همان رابط را دارند، به این معنی که می‌توان آنها را جایگزین یکدیگر کرد.
  • Decorator و Proxy هر دو به روش مشابهی ساخته شده‌اند، اما برای اهداف بسیار متفاوتی استفاده می‌شوند. هر دو الگو بر اساس اصل ترکیب بنا شده‌اند، که می‌گوید یک شیء باید بخشی از کار خود را به شیء دیگری محول کند. تفاوت اصلی این است که یک Proxy معمولاً چرخه حیات شیء سرویس خود را به‌تنهایی مدیریت می‌کند، در حالی که Decorators همیشه توسط مشتری مونتاژ می‌شوند.

نتیجه‌گیری

در این مقاله، درباره اینکه الگوی Proxy چیست، چه زمانی باید از آن استفاده کرد و مزایا و معایب استفاده از این الگوی طراحی صحبت کردیم. سپس برخی از موارد استفاده از این الگو و ارتباط آن با سایر الگوهای طراحی کلاسیک را بررسی کردیم.

شایان ذکر است که الگوی Proxy، همراه با سایر الگوهای طراحی ارائه‌شده توسط Gang of Four، یک درمان همه‌جانبه یا راه‌حل نهایی برای طراحی برنامه نیست. یک بار دیگر، این وظیفه مهندسان است که بررسی کنند چه زمانی باید از یک الگوی خاص استفاده کرد. در نهایت، این الگوها زمانی مفید هستند که به‌عنوان ابزاری دقیق استفاده شوند، نه به‌عنوان یک چکش سنگین.

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