Mastering Proxy Pattern in C# for Beginners

Özkan ARDİL
9 min readNov 9, 2023

--

Proxy Design Pattern in C#

Picture this…

You’re architecting a complex software system, and you need a guardian for your objects, a gatekeeper that controls access while adding layers of functionality.

That’s where the Proxy Design Pattern steps in.

In this article we’ll;

  • Dive into the heart of C# development,
  • Explore Proxy Design Pattern from class diagrams to real-world code examples,
  • Unveil its advantages,
  • Dissect the disadvantages,
  • Ensure it aligns with SOLID principles.

By the end, you’ll be the master of proxies, enhancing your software with finesse.

A proxy is basically a substitute for an intended object.

When a client deals with a proxy object, it thinks that it is dealing with the actual object. You need to support this kind of design because dealing with an original object is not always possible.

This is because of many factors such as security issues, for example.

If we want to give an example from the software world; An ATM implementation will hold proxy objects for bank information that exists on a remote server. In the real programming world, creating multiple instances of a complex object (a heavy object) is costly in general. So, whenever you can, you should create multiple proxy objects that can point to the original object. This mechanism can also help you to save the computer/system memory.

Proxy Design Pattern is used in three different situations.

  • Remote Proxy: can be used in situations where a remote object is to be used. It provides a local delegate to the remote object and allows us to perform the necessary checks.
  • Virtual Proxy: is preferred to the creation or use of costly objects to produce or use. As an example of this, we can give the image upload function, which is usually mentioned by everyone. It can be used to display the -loading- message given if a high-sized image is loaded late due to its size, and then the image as soon as the upload process is finished.
  • Protection Proxy: can be used in Authorization or login situations.

Class Diagram

You will find the class diagram below.

Class diagram of Proxy Design Pattern

Elements

  • Client,
  • The Subject is our Interface or abstract class that will enable the client to work with a single type. It is the structure from which our Real Subject and Proxy objects are derived.
  • Concrete Subject is our real object that will perform the actual work of the current work.
  • Proxy is our proxy class. It will respond to the client’s requests by carrying the Real Subject reference in it. Naturally, the client will be able to access the actual object indirectly through the proxy.

Solution Explorer

You will find the soluiton explorer screenshot below.

Solution explorer of Proxy Design Pattern sample project

Implementation

For simplicity, we’ve developed a sample project as a console application. In this example, we access the “ConcreteSubject” class through the “Proxy” class.

First, we create our Subject Class:

 public abstract class Subject
{
public abstract void DoSomeWork();
}

Next, we establish the real object, the “ConcreteSubject” class:

 public class ConcreteSubject : Subject
{
public override void DoSomeWork()
{
Console.WriteLine("This text comes from ConcreteSubject.DoSomeWork() method.");
}
}

Notice that “ConcreteSubject” inherits from “Subject,” and it’s the object that performs the actual operations.

Now, we’ll construct the proxy and create the “Proxy” class, which the client accesses:

 public class ProxyClass : Subject
{
Subject cs;
public override void DoSomeWork()
{
Console.WriteLine("Proxy call happening now...");
//Lazy initialization:We'll not instantiate until the method is
//called
if (cs == null)
{
cs = new ConcreteSubject();
}
cs.DoSomeWork();
}
}

The screenshot of the output is presented below.

The output of the Proxy Design Pattern project

Modified Implementation

The sample application we developed simulated accessing the concrete class through the proxy class. However, in real projects things are not that simple.

So, let’s create a new project by modifying our project a little to provide a more realistic example.

In this project, we will create two defined users. Only defined users will be able to access the main class. We will return an error message when access is attempted by undefined users.

By this way, I will try to implement a simple security layer using proxy design pattern.

First, we create our Subject Class:

 public abstract class SubjectModified
{
public abstract void DoSomeWork();
}

Next, we establish the modified real object, the “ConcreteSubjectModified” class:

 internal class ConcreteSubjectModified : SubjectModified
{
private string _user;

public ConcreteSubjectModified(string user)
{
_user = user;
}

public override void DoSomeWork()
{
Console.WriteLine($"User {_user} is authorized and this text comes from ConcreteSubjectModified.DoSomeWork() method for him.");
}
}

To customize the output message according to the current user, I modified the class by adding constructor.

Now, we’ll construct the modified proxy class with required business logic.

internal class ProxyModified : SubjectModified
{
SubjectModified cs;
string[] registeredUsers;
string currentUser;

public ProxyModified(string currentUser)
{
//Avoiding to instantiate inside the constructor
//cs = new ConcreteSubject();
//Registered users
registeredUsers = new string[] { "Admin", "Manager" };
this.currentUser = currentUser;
}

public override void DoSomeWork()
{
Console.WriteLine("\nProxy call happening now...");
Console.WriteLine("{0} wants to invoke a proxy method.", currentUser);

if (registeredUsers.Contains(currentUser))
{
//Lazy initialization: We'll not instantiate until the method is called
if (cs == null)
{
cs = new ConcreteSubjectModified(currentUser);
}
cs.DoSomeWork();
}
else
{
Console.WriteLine("Sorry {0}, you do not have access.", currentUser);
}
}

The screenshot of the output of the modified project is shown below.

The output of the modified proxy design pattern project

A protection proxy might be implemented like a decorator, but you should
not forget the intent of a proxy. Decorators focus on adding responsibilities, but proxies focus on controlling the access to an object.

Proxies differ from each other through their types and implementations.

So, if you can remember their purpose, in most cases you will be able to clearly distinguish proxies from decorators.

Source Code

You can access the source code of the project on my Design Patterns in C# GitHub repository.

There are two projects named as ProxyDesignPattern and ProxyDesignPatternModified. So, please refer to related project for the full source code.

If you can give the repository a star and share the article, you will support me in reaching more people.

Advantages of the Proxy Design Pattern

Lazy Loading: One of the primary advantages of the Proxy Design Pattern is its ability to facilitate lazy loading. Lazy loading means that the actual object is created and initialized only when it’s needed, rather than upfront. This can be highly beneficial in situations where object creation is resource-intensive. For example, consider an application with a large number of images. With a proxy, you can defer loading the actual image data until a user requests to view it. This can significantly improve performance and reduce resource consumption.

Access Control: Proxies are an effective way to enforce access control to objects. By using proxies, you can add an extra layer of security and authorization checks before allowing clients to access the real object. This is particularly useful in scenarios where certain operations should only be accessible to authorized users. For instance, in a content management system, a proxy can check user permissions before allowing them to modify sensitive content as we illustrate within our sample project above.

Caching: Proxies can implement caching mechanisms to store the results of expensive operations. When the same operation is requested again, the proxy can return the cached result instead of recomputing it. This can greatly improve application performance by reducing redundant calculations. For example, in a web application, a proxy can cache frequently accessed database query results or frequently used web service responses.

Remote Object Access: The Proxy Design Pattern is invaluable when dealing with remote object access. Proxies can act as remote proxies, allowing clients to interact with objects located in remote systems. This abstraction simplifies the communication process and makes it transparent to the client. For instance, consider a distributed system where a proxy is responsible for handling network communication and marshalling data between a local application and a remote server.

Disadvantages of the Proxy Design Pattern

Increased Complexity: One of the notable disadvantages of the Proxy Design Pattern is that it can introduce additional complexity to the system. When you introduce proxies, especially multiple types of proxies, it can make the code base more intricate. Managing these proxies and ensuring that they work correctly can be challenging. This added complexity may impact code maintainability and comprehension.

Performance Overhead: While proxies can enhance performance through lazy loading and caching, they can also introduce some performance overhead. The additional checks and indirection introduced by proxies can slow down certain operations. In scenarios where high-performance is critical, the use of proxies should be carefully considered to ensure they do not become bottlenecks.

Implementation Challenges: Designing and implementing proxies correctly can be challenging. It requires a deep understanding of the specific use case and the ability to create effective proxy classes that don’t violate the SOLID principles. Misimplementing a proxy can lead to unexpected behavior and can be challenging to debug.

Proxy Design Pattern and SOLID Principles in C#

In the context of the SOLID principles, let’s see how the Proxy Design Pattern aligns with each one:

Single Responsibility Principle (SRP): The Proxy Design Pattern adheres to the SRP by separating the responsibilities of the Real Subject and the Proxy. The Real Subject focuses on its primary functionality, while the Proxy handles additional responsibilities like access control, lazy loading, or caching. This separation ensures that each class has a single, well-defined responsibility.

Open-Closed Principle (OCP): The Proxy Design Pattern is open for extension and closed for modification. You can create new types of proxies with different behaviors without altering the Real Subject or the existing proxy classes. This extended capability aligns with the OCP by allowing you to add new features to the system without changing existing code.

Liskov Substitution Principle (LSP): The Proxy Design Pattern respects the LSP by substituting the Proxy for the Real Subject seamlessly. Clients interact with the Proxy as if it were the Real Subject, and the Proxy ensures that the substitution doesn’t affect the correctness of the program. This principle is crucial for maintaining compatibility and avoiding unexpected behavior when using proxies.

Interface Segregation Principle (ISP): When designing interfaces for proxies and Real Subjects, you can follow the ISP by creating specific interfaces tailored to their needs. This way, clients don’t have to depend on methods they don’t use, and you can design interfaces that make sense for each type of object, promoting a more cohesive and maintainable system.

Dependency Inversion Principle (DIP): The Proxy Design Pattern aligns with the DIP by introducing abstractions (interfaces) for both the Proxy and Real Subject. High-level modules that use the Proxy or Real Subject depend on these abstractions, allowing you to switch implementations easily. This promotes loose coupling and flexibility in the system’s design.

Conclusion

As we conclude our exploration of the Proxy Design Pattern in C#, it’s evident that Proxy is a versatile and valuable tool in your software development arsenal.

With an understanding of class diagrams, code samples, advantages, and considerations, I hope you’ve gained the expertise to employ Proxy effectively.

By adhering to SOLID principles, you’ll ensure that your designs are scalable and maintainable.

As you embark on future projects, remember that Proxy stands ready to enhance your software by controlling access to objects with finesse.

Keep coding, keep designing, and keep mastering the art of C# development.

👏 If you found the content useful, I would appreciate it if you could support it with applause (you can also contribute with more than one applause by holding down the clap button for a long time).

⬇️ Check out my other articles.

💬 Let me know in the comment section what have I missed? Where I was wrong?

👨‍👦‍👦 You can also share this article with your friend. Argue a little on it. It is known that the truth is born in a dispute.

🙃 Stay determined, stay focused, and keep going

Thanks for reading…

--

--

Özkan ARDİL

.NET C# JS and Angular dev with 8+ yrs exp, self-taught & passionate web developer. Sharing tips & experiences in C# and web dev.