Proxy
Provide a surrogate or placeholder for another object to control access to it.
Last updated
Provide a surrogate or placeholder for another object to control access to it.
Last updated
Proxy (also known as Surrogate) is a design pattern belonging to the Structural Pattern group.
Indirectly controls object access through a delegated object
Provides a representative class to manage access to members of another class
Solve security, performance, validation,...
Frequency of use: quite high
Suppose we have a problem accessing a large object. This object takes up a lot of system resources. We need it often, but not always. For example, when we query the database.
We can implement lazy initialization, which means only creating it when needed. At that time, the client who wants to access must run through this code, but the problem that arises is that the code will be duplicated
The best thing would probably be to include this line of code in the object itself. But if this class is a 3rd party, it's not possible.
Another issue in terms of security, or we want to validate it without the need for a client, like when uploading a file.
Proxy says that we need to create a new class that represents the existing service class with the same interface, this class is called proxy. Then when updating the application, it will pass the proxy object to all clients on the original object. When receiving a request from the client, the proxy creates a real service and delegates all tasks to it.
If something must be run before or after the main logic of the class, a proxy allows doing that without changing the class. Because Proxy implements the same interface as the main class, it can connect to any client waiting for communication from the real server.
Answer these two questions to decide whether to install a proxy or not:
Is there an “expensive resource” but is only initialized when it is needed?
Have a resource that needs to check security, availability or validation but don't want to do it on the client side?
=> Wrap 1 object and provide methods to access that object
Proxies solve the following problems:
There are situations where a client does not or cannot directly reference an Object, but still wants to interact with it.
The proxy object can act as an intermediary between the client and the target object.
The proxy object has the same interface as the target object.
The proxy keeps a reference to the target object and can forward requests to the target as required
Proxies are useful wherever there is a need for a more complex reference to an object than the simple pointer or simple reference that can be provided by the grantor.
A credit card is a proxy for a bank account, which is a proxy for a bundle of cash. Both implement the same interface: they can be used for making a payment. A consumer feels great because there’s no need to carry loads of cash around. A shop owner is also happy since the income from a transaction gets added electronically to the shop’s bank account without the risk of losing the deposit or getting robbed on the way to the bank.
Components in the model:
ServiceInterface: Defines a common interface for Service and Proxy so that Proxy can be used wherever Service is expected.
Service: Defines the ServiceInterface that the Proxy represents.
Proxy:
Maintains a reference that allows the Proxy to access the Service. Proxies can refer to ServiceInterface if Service and ServiceInterface are the same.
Provide interfaces similar to ServiceInterface so that Proxy can replace Service .
Controls access to the Service and is responsible for its creation and deletion.
A few other responsibilities depend on the type of Proxy:
Remote proxies are responsible for encoding a request and its arguments and sending the encrypted request to the Service in a different address space.
Virtual proxies can cache additional information about the Service to delay accessing it.
Protection proxies check whether the necessary access permissions are available to fulfill the request.
Advantage
Open/Closed Principle: You can add new proxies without changing services or clients.
Improve Performance through lazy loading.
It provides protection to the real object from the outside world.
Reduce costs when there are many accesses to objects with large initial initialization costs.
The code can become more complicated because you need to add new classes.
Response from the service may be delayed.
Below we can list some cases where we should consider using the Proxy pattern:
Lazy initialization (virtual proxy): When you have a heavy service object that wastes system resources by always being active, even though you only need it occasionally.
Access control (protection proxy): When you want only specific customers to be able to use the service object.
Local execution of a remote service (remote proxy): This is when the service object is placed on a remote server.
Logging requests (logging proxy): When you want to keep a history of requests to the service object.
Caching request results (caching proxy): When you need to store the results of client requests and manage the lifecycle of this cache, especially if the results are quite large.
Smart reference: When you need to remove a heavy object when no client is using it.
There are dozens of ways to utilize the Proxy pattern. Let’s go over the most popular uses.
Lazy initialization (virtual proxy). This is when you have a heavyweight service object that wastes system resources by being always up, even though you only need it from time to time.
Instead of creating the object when the app launches, you can delay the object’s initialization to a time when it’s really needed.
Access control (protection proxy). This is when you want only specific clients to be able to use the service object; for instance, when your objects are crucial parts of an operating system and clients are various launched applications (including malicious ones).
The proxy can pass the request to the service object only if the client’s credentials match some criteria.
Local execution of a remote service (remote proxy). This is when the service object is located on a remote server.
In this case, the proxy passes the client request over the network, handling all of the nasty details of working with the network.
Logging requests (logging proxy). This is when you want to keep a history of requests to the service object.
The proxy can log each request before passing it to the service.
Caching request results (caching proxy). This is when you need to cache results of client requests and manage the life cycle of this cache, especially if results are quite large.
The proxy can implement caching for recurring requests that always yield the same results. The proxy may use the parameters of requests as the cache keys.
Smart reference. This is when you need to be able to dismiss a heavyweight object once there are no clients that use it.
The proxy can keep track of clients that obtained a reference to the service object or its results. From time to time, the proxy may go over the clients and check whether they are still active. If the client list gets empty, the proxy might dismiss the service object and free the underlying system resources.
The proxy can also track whether the client had modified the service object. Then the unchanged objects may be reused by other clients.
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.
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.
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.
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.
Consider implementing lazy initialization for the service object.
Decorator and Proxy have similar structures, but very different intents. Both patterns are built on the composition principle, where one object is supposed to delegate some of the work to another. The difference is that a Proxy usually manages the life cycle of its service object on its own, whereas the composition of Decorators is always controlled by the client.