1
1
"""
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
+
2
13
*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.
4
16
"""
5
17
6
- import time
7
18
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 ()
8
31
9
- class SalesManager :
10
- def talk (self ):
11
- print ("Sales Manager ready to talk" )
12
32
33
+ class RealSubject (Subject ):
34
+ """
35
+ This is the main job doer. External services like payment gateways can be a
36
+ good example.
37
+ """
13
38
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 ):
15
44
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 )
25
56
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`.
28
76
77
+ >>> client(real_subject, 'admin')
78
+ I am doing the job for admin
29
79
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
+ """
35
83
36
84
37
85
if __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