Proxy Design Pattern
Video Lecture
Section | Video Links |
---|---|
Proxy Overview | |
Proxy Use Case | |
__class__ Attribute | |
Circular Imports |
Overview
The Proxy design pattern is a class functioning as an interface to another class or object.
A Proxy could be for anything, such as a network connection, an object in memory, a file, or anything else you need to provide an abstraction between.
Types of proxies,
-
Virtual Proxy: An object that can cache parts of the real object, and then complete loading the full object when necessary.
-
Remote Proxy: Can relay messages to a real object that exists in a different address space.
-
Protection Proxy: Apply an authentication layer in front of the real object.
-
Smart Reference: An object whose internal attributes can be overridden or replaced.
Additional functionality can be provided at the proxy abstraction if required. E.g., caching, authorization, validation, lazy initialization, logging.
The proxy should implement the subject interface as much as possible so that the proxy and subject appear identical to the client.
The Proxy Pattern can also be called Monkey Patching or Object Augmentation
Terminology
- Proxy: An object with an interface identical to the real subject. Can act as a placeholder until the real subject is loaded or as gatekeeper applying extra functionality.
- Subject Interface: An interface implemented by both the Proxy and Real Subject.
- Real Subject: The actual real object that the proxy is representing.
- Client: The client application that uses and creates the Proxy.
Proxy UML Diagram
Source Code
This concept example will simulate a virtual proxy. The real subject will be called via the proxy. The first time the request is made, the proxy will retrieve the data from the real subject. The second time it is called, it will return the data from the proxies own cache which it created from the first request.
./proxy/proxy_concept.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
Output
python ./proxy/proxy_concept.py
1848118706080
pulling data from RealSubject
[1, 2, 3]
pulling data from Proxy cache
[1, 2, 3]
SBCODE Editor
Example Use Case
In this example, I dynamically change the class of an object. So, I am essentially using an object as a proxy to other classes.
Every time the tell_me_the_future()
method is called; it will randomly change the object to use a different class.
The object PROTEUS
will then use the same static attributes and class methods of the new class instead.
Example UML Diagram
Source Code
./proxy/client.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
./proxy/interface_proteus.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
./proxy/lion.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
./proxy/serpent.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
./proxy/leopard.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Output
python ./proxy/client.py
I am the form of a Lion
I am the form of a Leopard
I am the form of a Serpent
I am the form of a Leopard
I am the form of a Lion
SBCODE Editor
New Coding Concepts
__class__ Attribute
You can change the class of an object by executing self.__class__ = SomeOtherClass
Note that doing this does not affect any attributes created during initialization, e.g. self.instance_attribute = 'abc'
, since the object itself hasn't changed. Only the references to its methods and static attributes have been replaced with the methods and static attributes of the new class.
This explains how calling tell_me_the_future()
and tell_me_your_form()
from the Proxy use case example, produced different results after changing self.__class__
Below is some code to demonstrate how changing an instances' __class__
does not affect any instance (self
) level attributes.
The class of the object A
is changed at runtime. The id
of the object A
remains the same. All methods have been updated, but self.instance_attribute
still equals a
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
Avoiding Circular Imports.
Normally in all the examples so far, I have been importing using the form
from module import Class
In ./proxy/client.py
I import the Lion
module. The Lion
module itself imports the Leopard
and Serpent
modules, that in turn also re-import the Lion
module again. This is a circular import and occurs in some situations when you separate your modules into individual files.
Circular imports will prevent the Python interpreter from compiling your .py
file into byte code.
The error will appear like,
cannot import name 'Lion' from partially initialized module 'lion' (most likely due to a circular import)
To avoid circular import errors, you can import modules using the form.
import module
And when the import is actually needed in some method,
OBJECT = module.ClassName
See the Lion, Serpent and Leopard classes for examples.
Summary
- Proxy forwards requests onto the Real Subject when applicable, depending on the kind of proxy.
- A virtual proxy can cache elements of a real subject before loading the full object into memory.
- A protection proxy can provide an authentication layer. For example, an NGINX proxy can add Basic Authentication restriction to an HTTP request.
- A proxy can perform multiple tasks if necessary.
- A proxy is different from an Adapter. The Adapter will try to adapt two existing interfaces together. The Proxy will use the same interface as the subject.
- It is also very similar to the Facade, except you can add extra responsibilities, just like the Decorator. The Decorator however can be used recursively.
- The intent of the Proxy is to provide a stand in for when it is inconvenient to access a real subject directly.
- The Proxy design pattern may also be called the Surrogate design pattern.