Implementing the Proxy Pattern in C#
The Proxy pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. This can be useful for various purposes, such as lazy initialization, access control, logging, and more.
Embark on a journey of continuous learning and exploration with DotNet-FullStack-Dev. Uncover more by visiting our https://dotnet-fullstack-dev.blogspot.com reach out for further information.
Why Use the Proxy Pattern?
- Control Access: Restrict access to certain objects.
- Lazy Initialization: Delay the creation and initialization of expensive objects until they are actually needed.
- Logging: Log requests and responses from objects.
- Remote Proxy: Represent objects that are in different address spaces.
- Caching: Cache results from expensive operations.
Example Scenario
Let’s consider a scenario where we have a Book
class that represents a book in a library system. The Book
class has a method GetBookDetails
that fetches book details from a database. This operation can be expensive, so we want to use a proxy to control access and add caching to improve performance.
Step-by-Step Implementation
Step 1: Define the Subject Interface
Define an interface that both the real subject and the proxy will implement.
public interface IBook
{
string GetBookDetails(string bookId);
}
Step 2: Create the Real Subject
Create the real subject class that implements the subject interface.
public class Book : IBook
{
public string GetBookDetails(string bookId)
{
// Simulate an expensive operation like fetching data from a database
Console.WriteLine("Fetching book details from database...");
System.Threading.Thread.Sleep(2000); // Simulate delay
return $"Book details for {bookId}";
}
}
Step 3: Create the Proxy Class
Create the proxy class that implements the subject interface and controls access to the real subject.
public class BookProxy : IBook
{
private readonly Book _realBook;
private readonly Dictionary<string, string> _cache;
public BookProxy()
{
_realBook = new Book();
_cache = new Dictionary<string, string>();
}
public string GetBookDetails(string bookId)
{
if (_cache.ContainsKey(bookId))
{
Console.WriteLine("Returning cached book details...");
return _cache[bookId];
}
Console.WriteLine("Fetching book details via proxy...");
var bookDetails = _realBook.GetBookDetails(bookId);
_cache[bookId] = bookDetails;
return bookDetails;
}
}
Step 4: Use the Proxy in the Application
Use the proxy in the application to control access to the real subject and add caching.
class Program
{
static void Main()
{
IBook bookProxy = new BookProxy();
// First call, fetches from the database
Console.WriteLine(bookProxy.GetBookDetails("1"));
// Second call, returns cached details
Console.WriteLine(bookProxy.GetBookDetails("1"));
// Fetches new book details from the database
Console.WriteLine(bookProxy.GetBookDetails("2"));
}
}
Detailed Explanation
- Subject Interface (
IBook
): This interface defines the methodGetBookDetails
that both the real subject and the proxy will implement. It ensures that the proxy can be used in place of the real subject. - Real Subject (
Book
): TheBook
class implements theIBook
interface and provides the actual implementation of theGetBookDetails
method. This method simulates an expensive operation by adding a delay. - Proxy (
BookProxy
): TheBookProxy
class also implements theIBook
interface. It contains an instance of the real subject (Book
) and a cache to store the results of previous requests. TheGetBookDetails
method in the proxy first checks the cache. If the details are already cached, it returns the cached details. Otherwise, it calls the real subject's method, caches the result, and then returns the details. - Application (
Program
): In theMain
method, we use theBookProxy
to get book details. The first call toGetBookDetails
fetches the details from the database and caches them. The second call returns the cached details, avoiding the expensive database operation. The third call fetches new book details for a different book and caches them.
Conclusion
The Proxy pattern is a versatile design pattern that can be used to control access to objects, add caching, lazy initialization, and more. By implementing a proxy, you can optimize performance and manage resources efficiently. This pattern is especially useful in scenarios where object creation is expensive, access needs to be controlled, or operations need to be logged or monitored.