Real-Time Examples of Virtual Proxy Design Pattern in C#

Virtual Proxy Design Pattern Real-Time Example in C#

In this article, I will discuss the Real-Time Example of a Virtual Proxy Design Pattern in C#. Please read our previous article, where we discussed the basic concepts of the Proxy Design Pattern in C# with Examples.

Virtual Proxy Design Pattern Real-Time Example in C#

The Virtual Proxy pattern is a proxy used to defer the creation and initialization of expensive objects until they are needed. This can help improve performance, especially during application startup or when dealing with resources that consume significant memory or computational power.

As we already discussed, a Virtual Proxy is a placeholder for objects that are expensive to create. The Real Object is only created when a client requests or accesses an object for the first time. Let us understand this with one Real-Time Example. Please have a look at the following image. On the right-hand side, you can see System A, which has one image (Tiger image) of 200 MB. On the left-hand side, you can see the client. In between the Client and System A, there is System B, which acts as the Virtual Proxy.

Proxy Design Pattern Real-time Example in C# - Virtual Proxy

Let’s say, for the first time, the Client sends a request to System B (Virtual Proxy) to display the Tiger Image. What the Virtual Proxy (i.e., System B) will do is first it will check whether the Real Image Object is there in the Virtual Proxy or not. If the Real Image Object is not there, then in step 1, it will create the Real Image Object and load the image from the disk, and in step 2, it will call the Display Image method on the Real Image object. Virtual Proxy also holds the Real Image Object, created in step 1. Step 1, i.e., creating the Real Image Object and loading the image from the disk, is expensive.

Let’s say the client makes the same request for the second time to the Virtual Proxy to display the Tiger Image. Now, the virtual proxy will check whether the Real Image Object is there or not, and it finds the Real Image Object is there in the Virtual Proxy (this is because, in the first request, the Virtual Proxy holds the Virtual Proxy). So, the virtual proxy will not execute step 1, i.e., it will not create the Real Image Object and load the image from the disk. Instead, it will use the existing Real Image Object and call the Display Image method, i.e., step 2. So, this way, using the Virtual Proxy Design Pattern, we can avoid creating an expensive object repeatedly.

Class or UML Diagram of the Virtual Proxy Design Pattern Example:

Let us see the class or UML Diagram of the above-discussed Virtual Proxy for Loading the Image and see the different components. Please have a look at the following diagram for a better understanding.

Implementation of Real-Time Example of Proxy Design Pattern in C#:

As you can see in the above Diagram, it involves four components. they are as follows:

  1. Subject (IImage): This interface defines the members that the RealSubject and Proxy classes will implement. In our example, it is going to be the IImage interface.
  2. RealSubject (RealImage): This will be a concrete class that we want to use more efficiently using the Virtual Proxy class. This class should implement the Subject Interface. In our example, it is going to be the RealImage class.
  3. Proxy (ProxyImage): This will be a class that holds a reference to the RealSubject class, i.e., RealImage, and can access RealSubjecr class members when required. This class also implements the same Subject (IImage) interface. In our example, it is going to be the ProxyImageclass.
  4. Client: This Client is going to be a class, and the client class is going to use the Virtual Proxy Class, i.e., ProxyImage.
Implementation of Virtual Proxy Design Pattern Real-Time Example using C#:

Let us implement the above-discussed Real-Time Example step by step using the Virtual Proxy Design Pattern in C#. The proxy design pattern involves four components: Subject, Real Object, Proxy Object, and Client. Let us proceed and implement these components one by one.

Step 1: Creating Subject Interface

This is going to be an interface. So, create an interface with the name IImage.cs and copy and paste the following code. This interface provides the functionalities that both Real Object and Proxy Object concrete classes implement. In our example, the interface defines one method, i.e., DisplayImage.

namespace VirtualProxyDesignPattern
{
    // The Subject interface declares common operations for both RealSubject and the Proxy. 
    // As long as the client works with RealSubject using this interface, 
    // you will be able to pass it a proxy instead of a real subject.
    public interface IImage
    {
        void DisplayImage();
    }
}
Step 2: Creating Real Subject

This will be a concrete class, which will implement the Subject Interface, i.e., the IImage interface, and provide the implementation for the DisplayImage method. So, create a class file named RealImage.cs and copy and paste the following code. The Parameterized Constructor of the RealImage class takes the file name as a parameter and then loads the file from the disk by calling the LoadImageFromDisk method from within the constructor.

using System;
namespace VirtualProxyDesignPattern
{
    // The RealSubject contains some core business logic. 
    // Usually, RealSubjects are capable of doing some useful work which may be very slow or sensitive 
    // A Proxy can solve these issues without any changes to the RealSubject's code.
    public class RealImage : IImage
    {
        private string Filename { get; set; }
        public RealImage(string filename)
        {
            Filename = filename;
            LoadImageFromDisk();
        }
        public void LoadImageFromDisk()
        {
            Console.WriteLine("Loading Image : " + Filename);
        }
        public void DisplayImage()
        {
            Console.WriteLine("Displaying Image : " + Filename);
        }
    }
}

Note: Here, the object creation process is an expensive operation. This is because it will load the image from the disk at the time of object creation. The LoadImageFromDisk method is used to load the image from the disk. The DisplayImage method is used to display the image.

Step 3: Creating Proxy Object

This will be a concrete class, and it also implements the Subject Interface, i.e., the IImage interface, and provides the implementation for the DisplayImage method. So, create a class file named ProxyImage.cs and copy and paste the following code. As part of the DisplayImage method, first, we check whether the realImage instance is null. If null, we are creating the instance, and then on the realImage instance, we are calling the DisplayImage method. On the other hand, if the realImage instance is not null, it will not create the instance; instead, it will use the existing realImage instance to call the DisplayImage method.

namespace VirtualProxyDesignPattern
{
    // The Proxy has an interface identical to the RealSubject.
    public class ProxyImage : IImage
    {
        private RealImage realImage = null;
        private string Filename { get; set; }
        public ProxyImage(string filename)
        {
            Filename = filename;
        }
        public void DisplayImage()
        {
            if (realImage == null)
            {
                realImage = new RealImage(Filename);
            }
            realImage.DisplayImage();
        }
    }
}
Step 4: Client

In our example, the Main method of the Program class is going to be the Client. So, please modify the Main method of the Program class as shown below. First, we create the object of ProxyImage to display the Tiger Image and then call the DisplayImage method three times. In this case, the first call to the DisplayImage method will create the RealImage instance, and hence, it will load the image from the disk. But from the 2nd call onwards to the DisplayImage method, it will use the existing RealImage instance, and hence, it will not load the image from the disk. This process is the same for the 2nd proxy object creation to display the Lion Image.

using System;
namespace VirtualProxyDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            IImage Image1 = new ProxyImage("Tiger Image");
            
            Console.WriteLine("Image1 calling DisplayImage first time :");
            Image1.DisplayImage(); // loading necessary

            Console.WriteLine("Image1 calling DisplayImage second time :");
            Image1.DisplayImage(); // loading unnecessary

            Console.WriteLine("Image1 calling DisplayImage third time :");
            Image1.DisplayImage(); // loading unnecessary

            Console.WriteLine();
            IImage Image2 = new ProxyImage("Lion Image");

            Console.WriteLine("Image2 calling DisplayImage first time :");
            Image2.DisplayImage(); // loading necessary

            Console.WriteLine("Image2 calling DisplayImage second time :");
            Image2.DisplayImage(); // loading unnecessary

            Console.ReadKey();
        }
    }
}
Output:

Implementation of Virtual Proxy Real-Time Example in C#

When to use Virtual Proxy Design Pattern in C#?

The Virtual Proxy Design Pattern is beneficial when an object is expensive to create, whether in terms of computational resources, memory, or even time. Using a virtual proxy, you can defer the creation and initialization of these expensive objects until they are truly needed. Here are common scenarios in which the Virtual Proxy pattern can be particularly helpful:

  • Lazy Initialization: If an application contains heavy objects not always used when it starts, you can delay their instantiation to improve startup performance. The Virtual Proxy will create the object only when accessed for the first time.
  • Resource Management: For applications that manage resources like large images, videos, or complex data structures, it’s impractical to load all resources simultaneously, especially if the user might not access all of them. A Virtual Proxy can ensure that resources are loaded on demand.
  • Remote Resources: If you’re dealing with resources from remote sources (like a network or the internet), loading these resources might be slow or even temporarily impossible due to network latency or failures. A Virtual Proxy can handle this by attempting to load the resource only when required, potentially providing feedback or fallback behavior if the resource is unavailable.
  • Access to Expensive Services: If you’re accessing a third-party service or API with usage limitations or associated costs, you can use a Virtual Proxy to minimize unnecessary access or batch requests.
  • Database Access: When dealing with large datasets or databases, loading all data simultaneously might be inefficient, especially if the user only needs a subset. A Virtual Proxy can act as a placeholder for the data, fetching it only when required. This pattern is particularly prevalent in ORM (Object-Relational Mapping) tools, which load database entities lazily.
  • Placeholder for Future Results: In applications that perform extensive computations that might take time to complete, a Virtual Proxy can act as a placeholder for the expected result, computing the result only when it’s accessed.
Advantages and Disadvantages of Virtual Proxy Design Pattern in C#

The Virtual Proxy Design Pattern provides a way to access an object indirectly, allowing for operations like lazy initialization, access control, and other operations to be inserted between the client and the real object.

Advantages of Virtual Proxy Design Pattern in C#:
  • Lazy Initialization: One of the main advantages of using a virtual proxy is that it can create objects on demand. This can improve system performance if creating the object is costly and the object is not always needed.
  • Separation of Concerns: The proxy pattern helps keep the code related to access control or other intermediary tasks separated from the actual implementation of the class.
  • Memory Efficiency: If you have heavyweight objects that consume a lot of resources, virtual proxies can be useful to delay their instantiation until they are truly necessary.
  • Flexibility: Proxies can introduce additional functionality like logging, caching, etc., without changing the real object’s code.
  • Protection: Proxies can control access to the real object, acting as a protective barrier, ensuring that the real object operates within certain constraints.
Disadvantages of Virtual Proxy Design Pattern in C#:
  • Overhead: Introducing proxies can add an additional layer of complexity and may incur some overhead, especially if not really needed.
  • Maintenance: If the interface of the real subject changes, the proxy needs to be updated as well, leading to increased maintenance effort.
  • Complexity: Introducing another level of indirection can make the system more complicated and harder to understand for developers unfamiliar with the pattern.
  • Latency: While the lazy initialization can benefit, it can also lead to unexpected latency when the real object is finally accessed. This behavior might be undesirable when consistent response times are needed.
  • Misuse: It can be overengineering if used without a valid reason. Not every object requires a proxy, and weighing the benefits against the complexity introduced is important.

In C# or any other language, the Virtual Proxy Design Pattern should be driven by a clear need, such as when an operation is resource-intensive or needs controlled access. As with any design pattern, it’s crucial to understand the problem you’re trying to solve and evaluate whether the pattern is appropriate.

In the next article, I will discuss the Real-Time Examples of Remote Proxy Design Patterns in C#. Here, in this article, I try to explain the Real-Time Example of the Virtual Proxy Design Pattern in C#. I hope you enjoy this Virtual Proxy Design Pattern Real-Time Example in C# article.

Leave a Reply

Your email address will not be published. Required fields are marked *