11"""
2+ *What is this pattern about?
3+ Proxy is used in places where you want to add functionality to a class without
4+ changing its interface. The main class is called `Real Subject`. A client should
5+ use the proxy or the real subject without any code change, so both must have the
6+ same interface. Logging and controlling access to the real subject are some of
7+ the proxy pattern usages.
8+
9+ *References:
10+ https://refactoring.guru/design-patterns/proxy/python/example
11+ https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Fronting.html
12+
213*TL;DR
3- Provides an interface to resource that is expensive to duplicate.
14+ Add functionality or logic (e.g. logging, caching, authorization) to a resource
15+ without changing its interface.
416"""
517
6- import time
718
19+ class Subject :
20+ """
21+ As mentioned in the document, interfaces of both RealSubject and Proxy should
22+ be the same, because the client should be able to use RealSubject or Proxy with
23+ no code change.
24+
25+ Not all times this interface is necessary. The point is the client should be
26+ able to use RealSubject or Proxy interchangeably with no change in code.
27+ """
28+
29+ def do_the_job (self , user ):
30+ raise NotImplementedError ()
831
9- class SalesManager :
10- def talk (self ):
11- print ("Sales Manager ready to talk" )
1232
33+ class RealSubject (Subject ):
34+ """
35+ This is the main job doer. External services like payment gateways can be a
36+ good example.
37+ """
1338
14- class Proxy :
39+ def do_the_job (self , user ):
40+ print (f'I am doing the job for { user } ' )
41+
42+
43+ class Proxy (Subject ):
1544 def __init__ (self ):
16- self .busy = 'No'
17- self .sales = None
18-
19- def talk (self ):
20- print ("Proxy checking for Sales Manager availability" )
21- if self .busy == 'No' :
22- self .sales = SalesManager ()
23- time .sleep (0.1 )
24- self .sales .talk ()
45+ self ._real_subject = RealSubject ()
46+
47+ def do_the_job (self , user ):
48+ """
49+ logging and controlling access are some examples of proxy usages.
50+ """
51+
52+ print (f'[log] Doing the job for { user } is requested.' )
53+
54+ if user == 'admin' :
55+ self ._real_subject .do_the_job (user )
2556 else :
26- time .sleep (0.1 )
27- print ("Sales Manager is busy" )
57+ print (f'[log] I can do the job just for `admins`.' )
58+
59+
60+ def client (job_doer , user ):
61+ job_doer .do_the_job (user )
62+
63+ def main ():
64+ """
65+ >>> proxy = Proxy()
66+
67+ >>> real_subject = RealSubject()
68+
69+ >>> client(proxy, 'admin')
70+ [log] Doing the job for admin is requested.
71+ I am doing the job for admin
72+
73+ >>> client(proxy, 'anonymous')
74+ [log] Doing the job for anonymous is requested.
75+ [log] I can do the job just for `admins`.
2876
77+ >>> client(real_subject, 'admin')
78+ I am doing the job for admin
2979
30- class NoTalkProxy (Proxy ):
31- def talk (self ):
32- print ("Proxy checking for Sales Manager availability" )
33- time .sleep (0.1 )
34- print ("This Sales Manager will not talk to you" , "whether he/she is busy or not" )
80+ >>> client(real_subject, 'anonymous')
81+ I am doing the job for anonymous
82+ """
3583
3684
3785if __name__ == '__main__' :
38- p = Proxy ()
39- p .talk ()
40- p .busy = 'Yes'
41- p .talk ()
42- p = NoTalkProxy ()
43- p .talk ()
44- p .busy = 'Yes'
45- p .talk ()
46-
47- ### OUTPUT ###
48- # Proxy checking for Sales Manager availability
49- # Sales Manager ready to talk
50- # Proxy checking for Sales Manager availability
51- # Sales Manager is busy
52- # Proxy checking for Sales Manager availability
53- # This Sales Manager will not talk to you whether he/she is busy or not
54- # Proxy checking for Sales Manager availability
55- # This Sales Manager will not talk to you whether he/she is busy or not
86+ import doctest
87+ doctest .testmod ()
0 commit comments