TypeScript Proxy Design Pattern
What is proxy design pattern?
The proxy design pattern, classified as a structural design pattern, serves as an intermediary within a system. With the proxy design pattern, the communication between objects can be performed through third-party intermediate objects. Using these proxy objects, the actions of the system can be controlled and altered before or after the request from the client reaches the objects.
There are several terminologies in the proxy design pattern. These are:
- Service Interface: It’s contains all the properties of the service.
- Service: It is the class that contains the implementation of the service interface; it contains the business logic of the service interface.
- Proxy: It has a reference to the service objects. After processing requests, it passes the request to the service object.
- Client: This refers to the application or function that communicates with the system.
When should the proxy design pattern be used?
The proxy design pattern should be used when there is a need to control the objects’ access to higher-level services. If the higher-level service objects consume a vast amount of system resources, using the proxy design pattern allows the access and initialization of these services to be restricted to less resource-consuming third-party objects.
Another situation where the proxy design pattern could be useful is when there is a need to restrict the access of objects of lower classes to service classes.
If the service objects reside on a remote server or in third-party libraries, communication between clients and the service can be carried out via the proxy design pattern. At the same time, the history of the communication can be recorded and logged.
With the proxy design pattern, service objects can be tracked, and when the client no longer needs the objects, related objects can be dismissed, reducing the consumption of the service.
How to implement proxy design pattern in TypeScript
Let’s apply the proxy design pattern to TypeScript. First, let’s imagine a scenario where we are trying to retrieve users from a remote server. We design our system to continuously retrieve users from the server, maintaining an active connection to the remote server, which causes our system to waste a vast amount of resources. This basically forms a continuous, straight connection with the remote server, thereby losing the ability to alter data during the retrieval process.
After considering these issues, we decide to change our system design and employ the proxy design pattern. With this method, we now use third-party objects to restrict access to the remote server, accomplishing a more secure system and gaining the ability to manipulate data during the retrieval process. And with the proxy objects, we reduce our system resource consumption by breaking the constant communication link between the system and the remote server.
Proxy desing pattern diagram
Proxy desing pattern code
// Service interface
interface UserService {
getUserById(userId: number): string;
}
// Service
class RealUserService implements UserService {
getUserById(userId: number): string {
return `User#${userId}`;
}
}
// Proxy
class UserProxyService implements UserService {
private realUserService: RealUserService;
constructor() {
this.realUserService = new RealUserService();
}
getUserById(userId: number): string {
console.log(`Proxy: Before calling RealUserService.getUserById for userId ${userId}`);
const user = this.realUserService.getUserById(userId);
console.log(`Proxy: After calling RealUserService.getUserById for userId ${userId}`);
return user;
}
}
// Client
const userService = new UserProxyService();
const userResult = userService.getUserById(123);
console.log(`Result: ${userResult}`);
/*
Proxy: Before calling RealUserService.getUserById for userId 123
Proxy: After calling RealUserService.getUserById for userId 123
Result: User#123
*/