mirror of
https://github.com/rjNemo/design-patterns
synced 2026-06-06 02:26:40 +00:00
Proxy (#13)
* doc: add proxy README documentation * doc: edit general README files * add proxy code example Co-authored-by: Ruidy <r.nemausat@empfohlen.de>
This commit is contained in:
parent
664b39c32e
commit
18dc62563c
8 changed files with 110 additions and 0 deletions
|
|
@ -17,3 +17,4 @@
|
||||||
- [Decorator](structural/decorator/README.md)
|
- [Decorator](structural/decorator/README.md)
|
||||||
- [Facade](structural/facade/README.md)
|
- [Facade](structural/facade/README.md)
|
||||||
- [Flyweight](structural/flyweight/README.md)
|
- [Flyweight](structural/flyweight/README.md)
|
||||||
|
- [Proxy](structural/proxy/README.md)
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@ Structural patterns explain how to assemble objects and classes into larger stru
|
||||||
- [Decorator](decorator/README.md)
|
- [Decorator](decorator/README.md)
|
||||||
- [Facade](facade/README.md)
|
- [Facade](facade/README.md)
|
||||||
- [Flyweight](flyweight/README.md)
|
- [Flyweight](flyweight/README.md)
|
||||||
|
- [Proxy](proxy/README.md)
|
||||||
|
|
|
||||||
29
structural/proxy/README.md
Normal file
29
structural/proxy/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Proxy
|
||||||
|
|
||||||
|
Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Why would you want to control access to an object? Here is an example: you have a massive object that consumes a vast amount of system resources. You need it from time to time, but not always.
|
||||||
|
|
||||||
|
You could implement lazy initialization: create this object only when it’s actually needed. All of the object’s clients would need to execute some deferred initialization code. Unfortunately, this would probably cause a lot of code duplication.
|
||||||
|
|
||||||
|
In an ideal world, we’d want to put this code directly into our object’s class, but that isn’t always possible. For instance, the class may be part of a closed 3rd-party library.
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
The Proxy pattern suggests that you create a new proxy class with the same interface as an original service object. Then you update your app so that it passes the proxy object to all of the original object’s clients. Upon receiving a request from a client, the proxy creates a real service object and delegates all the work to it.
|
||||||
|
|
||||||
|
But what’s the benefit? If you need to execute something either before or after the primary logic of the class, the proxy lets you do this without changing that class. Since the proxy implements the same interface as the original class, it can be passed to any client that expects a real service object.
|
||||||
|
|
||||||
|
## How to Implement
|
||||||
|
|
||||||
|
1. If there’s no pre-existing service interface, create one to make proxy and service objects interchangeable. Extracting the interface from the service class isn’t always possible, because you’d need to change all of the service’s clients to use that interface. Plan B is to make the proxy a subclass of the service class, and this way it’ll inherit the interface of the service.
|
||||||
|
|
||||||
|
1. Create the proxy class. It should have a field for storing a reference to the service. Usually, proxies create and manage the whole life cycle of their services. On rare occasions, a service is passed to the proxy via a constructor by the client.
|
||||||
|
|
||||||
|
1. Implement the proxy methods according to their purposes. In most cases, after doing some work, the proxy should delegate the work to the service object.
|
||||||
|
|
||||||
|
1. Consider introducing a creation method that decides whether the client gets a proxy or a real service. This can be a simple static method in the proxy class or a full-blown factory method.
|
||||||
|
|
||||||
|
1. Consider implementing lazy initialization for the service object.
|
||||||
0
structural/proxy/__init__.py
Normal file
0
structural/proxy/__init__.py
Normal file
25
structural/proxy/main.py
Normal file
25
structural/proxy/main.py
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
from structural.proxy.proxy import Proxy
|
||||||
|
from structural.proxy.real_subject import RealSubject
|
||||||
|
from structural.proxy.subject import Subject
|
||||||
|
|
||||||
|
|
||||||
|
def client_code(subject: Subject) -> None:
|
||||||
|
"""
|
||||||
|
The client code is supposed to work with all objects (both subjects and
|
||||||
|
proxies) via the Subject interface in order to support both real subjects
|
||||||
|
and proxies. In real life, however, clients mostly work with their real
|
||||||
|
subjects directly. In this case, to implement the pattern more easily, you
|
||||||
|
can extend your proxy from the real subject's class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
subject.request()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("Client: Executing the client code with a real subject:")
|
||||||
|
real_subject = RealSubject()
|
||||||
|
client_code(real_subject)
|
||||||
|
print('')
|
||||||
|
print("Client: Executing the same client code with a proxy:")
|
||||||
|
proxy = Proxy(real_subject)
|
||||||
|
client_code(proxy)
|
||||||
28
structural/proxy/proxy.py
Normal file
28
structural/proxy/proxy.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
from structural.proxy.real_subject import RealSubject
|
||||||
|
from structural.proxy.subject import Subject
|
||||||
|
|
||||||
|
|
||||||
|
class Proxy(Subject):
|
||||||
|
"""The Proxy has an interface identical to the RealSubject."""
|
||||||
|
|
||||||
|
def __init__(self, real_subject: RealSubject) -> None:
|
||||||
|
self._real_subject = real_subject
|
||||||
|
|
||||||
|
def request(self) -> None:
|
||||||
|
"""
|
||||||
|
The most common applications of the Proxy pattern are lazy loading,
|
||||||
|
caching, controlling the access, logging, etc. A Proxy can perform one
|
||||||
|
of these things and then, depending on the result, pass the execution to
|
||||||
|
the same method in a linked RealSubject object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.check_access():
|
||||||
|
self._real_subject.request()
|
||||||
|
self.log_access()
|
||||||
|
|
||||||
|
def check_access(self) -> bool:
|
||||||
|
print("Proxy: Checking access prior to firing a real request.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def log_access(self) -> None:
|
||||||
|
print("Proxy: Logging the time of request.", end="")
|
||||||
13
structural/proxy/real_subject.py
Normal file
13
structural/proxy/real_subject.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from structural.proxy.subject import Subject
|
||||||
|
|
||||||
|
|
||||||
|
class RealSubject(Subject):
|
||||||
|
"""
|
||||||
|
The RealSubject contains some core business logic. Usually, RealSubjects are
|
||||||
|
capable of doing some useful work which may also be very slow or sensitive -
|
||||||
|
e.g. correcting input data. A Proxy can solve these issues without any
|
||||||
|
changes to the RealSubject's code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def request(self) -> None:
|
||||||
|
print("RealSubject: handling request.")
|
||||||
13
structural/proxy/subject.py
Normal file
13
structural/proxy/subject.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from abc import abstractmethod, ABC
|
||||||
|
|
||||||
|
|
||||||
|
class Subject(ABC):
|
||||||
|
"""
|
||||||
|
The Subject interface declares common operations for both RealSubject and
|
||||||
|
the Proxy. As long as the client works with RealSubject using this
|
||||||
|
interface, you'll be able to pass it a proxy instead of a real subject.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def request(self) -> None:
|
||||||
|
pass
|
||||||
Loading…
Reference in a new issue