design pattern

Structural Design Patterns: Proxy

Today we’re looking at the last of the Structural design patterns within our comprehensive Guide to Software Design Patterns series. The proxy design pattern is intended to act as a simple wrapper for another object. The proxy object can be directly accessed by the user and can perform logic or configuration changes required by the underlying subject object, without giving the client direct access to said subject.

In this article we’ll examine the proxy design pattern in more detail, looking at a real world example, along with a fully-functional C# code sample that will help to illustrate how the pattern might be implemented, so let’s get started!

In the Real World

In the real world, many of us use a proxy pattern on a daily basis: Every time we hand our debit card to the barista or allow Amazon to charge our credit card for that next sweet bargain, we’re using a dominant (and powerful) proxy design pattern found throughout the modern world. A debit card is merely a representational proxy of a bank account. Money orders and checks act in much the same manner, which Jerry Seinfeld explores in one of his great bits: “A check is like a note from your mother that says, ‘I don’t have any money, but if you contact these people I’m sure they’ll stick up for me. If you could just trust me this one time. I don’t have any money, but I have these. I wrote on these… Is this of any value at all?'”

Just like checks, your credit card acts as a proxy for your bank account. It’s a (relatively) secure way for a client (such as your favorite local coffee shop) to post a charge to your underlying account, without allowing that vendor direct access to said account. This kind of system is used all the time due to its simplicity and ease of use.

How It Works In Code

For the proxy design pattern code sample we’ll stick with the credit card example above and implement a few classes that illustrate the basic concepts that a client would use to interact with your underlying account by going through a proxy of a credit or debit card. For simplicity, we’ll start with the full working code sample below, after which we’ll explore it in more detail:

The basic structure of a proxy pattern consists of three components:

  • Subject: The baseline interface that defines the fundamental components of the subject in question.
  • RealSubject: The actual subject class that implements the Subject interface, and which the Proxy class will access behind the scenes and “wrap” to enable easier client interaction.
  • Proxy: The wrapper class that enables the client to perform actions upon the underlying RealSubject object, without performing any direct actions upon it.

Therefore, we begin by implementing the Subject interface, which we’re calling IAccount to represent a bank account. For this exceptionally simple example we’re just tracking one property (Balance) and one method (ChargeAccount(decimal amount)), which are both self-explanatory in function:

Now we need to create a RealSubject class that will implement the IAccount interface, so here we have the Account class:

Note: It’s worth pointing out that we’ve elected to specify a default balance value of $1000 for this example, whereas this amount would obviously be grabbed from a data service in production code.

Most of the logic takes place in the ChargeAccount(decimal amount) method, where we check if the Balance is greater than or equal to the charge amount, and either reduce the balance appropriately and issue a success message, or issue a failure message.

Finally, we need to create at least one Proxy class that will attach itself to our RealSubject class (Account) in some way, and then perform actions that “wrap” those underlying class methods. For this example we’ve actually defined two proxy classes with CreditCard and DebitCard, which both perform similar actions. However, their major difference is that CreditCard allows charge attempts to exceed the current Balance by performing an overcharge (i.e. simulating a credit card), whereas the DebitCard class prevents charges that would exceed the current Balance.

In both cases, it’s important that we access an Account class instance, so we’ve defined two constructors, one where an Account instance can be passed, and the other where a new Account instance is generated during construction:

Now, to make use of the proxy pattern classes we start by instantiating one of our proxy classes. We’ll begin with the DebitCardTest() method:

As mentioned, we first create an instance of DebitCard() (which creates a new, associated instance of Account), then we attempt three charges to the account. Since this is the DebitCard proxy class, our expectation is that an attempt to overcharge the account will result in a failed transaction. Sure enough, the log output shows our two successful charges followed by the third failed charge:

Now let’s try the CreditCardTest() method, which performs the same series of charges, but should allow our final charge to succeed by performing an overcharge:

As expected, the output shows that all three charges succeeded, however, since the last charge resulted in an overcharge (which is explicitly allowed by the CreditCard proxy class), the final account balance is negative $57.60.

This was a tiny sample of what the proxy design pattern can do, but should help to give a sense of its potential. Feel free to check out more design patterns in our ongoing design pattern series here!