We will take up a payment use case to demonstrate a real-world scenario for the Proxy pattern. Let's say that you go to shop at a mall and like a nice denim shirt there. You would like to purchase the shirt but you don't have enough cash to do so.
In yesteryears, you'd go to an ATM, take out the money, then come to the mall, and pay for it. Even earlier, you had a bank check for which you had to go to the bank, withdraw money, and then come back to pay for your expense.
Thanks to the banks, we now have something called a debit card. So now, when you want to purchase something, you present your debit card to the merchant. When you punch in your card details, the money is debited in the merchant's account for your expense.
Let's develop an application in Python v3.5 and implement the above use case. We start with the client first. You went to the shopping mall and now would like to purchase a nice denim shirt. Lets see how Client
code is written:
You
class—the clientmake_payment()
method is provided by the class__init__()
method calls the Proxy and instantiates itmake_payment()
method invokes the Proxy's method internally to make the payment__del__()
method returns in case the payment is successfulThus, the code example is as follows:
class You: def __init__(self): print("You:: Lets buy the Denim shirt") self.debitCard = DebitCard() self.isPurchased = None def make_payment(self): self.isPurchased = self.debitCard.do_pay() def __del__(self): if self.isPurchased: print("You:: Wow! Denim shirt is Mine :-)") else: print("You:: I should earn more :(") you = You() you.make_payment()
Now let's talk about the Subject
class. As we know, the Subject
class is an interface that is implemented by the Proxy
and RealSubject
.
Payment
class. It is an abstract base class and represents an interface.Payment
has the do_pay()
method that needs to be implemented by the Proxy
and RealSubject
.Let's see these methods in action in the following code:
from abc import ABCMeta, abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def do_pay(self): pass
We also developed the Bank
class that represents RealSubject
in this scenario:
Bank
will actually make the payment from your account in the merchant's account.Bank
has multiple methods to process the payment. The setCard()
method is used by the Proxy
to send the debit card details to the bank.__getAccount()
method is a private method of Bank
that is used to get the account details of the debit card holder. For simplicity, we have enforced the debit card number to be the same as the account number.Bank
also has the __hasFunds()
method to see if the account holder has enough funds in the account to pay for the shirt.do_pay()
method that is implemented by the Bank
class (from the Payment interface) is actually responsible for making the payment to the merchant based on available funds:class Bank(Payment): def __init__(self): self.card = None self.account = None def __getAccount(self): self.account = self.card # Assume card number is account number return self.account def __hasFunds(self): print("Bank:: Checking if Account", self.__getAccount(), "has enough funds") return True def setCard(self, card): self.card = card def do_pay(self): if self.__hasFunds(): print("Bank:: Paying the merchant") return True else: print("Bank:: Sorry, not enough funds!") return False
Let's now understand the last piece, which is the Proxy
:
DebitCard
class is the Proxy
here. When You
wants to make a payment, it calls the do_pay()
method. This is because You
doesn't want go to the bank to withdraw money and pay the merchant.DebitCard
class acts as a surrogate for the RealSubject
, Bank
.payWithCard()
method internally controls the object creation of RealSubject
, the Bank
class, and presents the card details to Bank
.Bank
goes through the internal checks on the account and does the payment, as described in previous code snippet:class DebitCard(Payment): def __init__(self): self.bank = Bank() def do_pay(self): card = input("Proxy:: Punch in Card Number: ") self.bank.setCard(card) return self.bank.do_pay()
For a positive case, when funds are enough, the output is as follows:
For a negative case—insufficient funds—the output is as follows:
18.223.33.157