Proxy Design Pattern, the right pattern for those people who act on behalf of another person.
In this article we will talk about another important structural design patterns: the?"Proxy?Pattern"?(.1).
Proxy is a structural design pattern that lets us provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing us to perform something either before or after the request gets through to the original object.
But why would we want to control access to an object? For an examples let’s consider that we need to support resource-hungry objects, and we do not want to instantiate such objects until they are actually requested by the client.
We 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.
So the right solution is to design a proxy object that instantiates the real object the first time the client makes a request of the proxy, remembers the identity of this real object, and then all subsequent requests are simply forwarded directly to the encapsulated real object.
The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate. In short, a proxy is a wrapper object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or it can provide additional logic. In the proxy, extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked. For the client, usage of a proxy object is similar to using the real object, because both implement the same interface.
A real-world analogy can be a "bank cheque" or a "credit card" that are a proxy for what is in our bank account. They can be used in place of cash, and provide a means of accessing that cash when required. And that’s exactly what the Proxy pattern does: "Controls and manages access to the object they are protecting".
A consumer feels great because there’s no need to carry much cash around. A shop owner is also happy since the income from a transaction gets added electronically to the shop bank account without the risk of losing the deposit or getting robbed on the way to the bank.
Another nice real-world analogy is the one represented in figure 1.
Let's imagine we are movie stars. We are so famous that everyone wants to work with us. We never lack acting opportunities. We get so many requests that we need an agent to handle them. Here's what we want our agent to do.
When a new filming request comes, the agent first checks to see if it fits our schedule. If so, the agent then checks our preferences (for example we prefer an action movie than a drama or a comedy).
Finally, if the new film fits both our schedule and our preferences, the agent sends us the request.
The agent is essentially a proxy that works for us.
Well, let's go back to the software world and see the structure of this pattern:
From the class diagram we identify four participants in the pattern.
From the sequential diagram we see that the Proxy forwards requests to RealSubject when appropriate, depending on the kind of proxy.
There are four common situations in which the Proxy pattern is applicable.
A) When we have to open an "expensive to create" objects. The real object is only created when a client first requests/accesses the object (we will see a smart ImageLoader for a Document Editor).
B) When we want to control the access to an object. (we will see an example where we want introduce lazy initialization and caching to a 3rd-party library).
C) When we want to implement access protection policies to certain objects (or classes), subsequently to the data and resources that these they represent. (we will see a conceptual example of a ParentalControl Component for an Internet Browser)
D) When we want to make it easier to access a remote object (we will see a couple of advanced application on Android Platform in next article.)
Let's start with the first one and consider the following problem:
We have a document editor that can embed graphical objects in a document. Some graphical objects, like large raster images, can be expensive to create. But opening a document should be fast, so we should avoid creating all the expensive objects at once when the document is opened. This isn't necessary anyway, because not all of these objects will be visible in the document at the same time. And moreover if an image appears twice in the document, we would like to load it only once.
The solution is to use another object, an image proxy, to act as a substitute for the real image. The proxy acts just like the image and takes care of instantiating it when required.
Figure 3 shows the class diagram and the sequence diagram of the solution of the problem using the proxy pattern.
The image proxy creates the real image only when the document editor asks it to display itself by invoking its "draw" operation. The proxy forwards subsequent requests directly to the image. It must therefore keep a reference to the image after creating it.
Let's assume that images are stored in separate files. In this case we can use the file name as the reference to the real object.?
The document editor accesses embedded images through the interface defined by the abstract Image class. Let's see the code below.
"ProxyImage" is a class for images that are created on demand. ProxyImage maintains the file name as a reference to the image on disk. The file name is passed as an argument to the ProxyImage constructor.
ProxyImage also stores a reference to the real Image instance. This reference won't be valid until the proxy instantiates the real image. The "draw" operation makes sure the image is instantiated before forwarding it the request. Let's see the snippet code below.
"RealImage" class it is the real service that knows how to load the image from the disk and knows how to draw it.
The unit test shows how to use the?ProxyImage?to get object of?RealImage?class when required. As we can see the image is attached into the text document using the "createImage" method but it is actually loaded from disk only the first time the "draw" method is called. See the snippet code below.
And now let's consider as a second example the case of a 3rd-party TED integration library that provides a service class to access to web services for obtaining the most popular TED videos list names and allowing to download a video by specifying its name (.2). However, it’s very inefficient, in fact, if the client application requests the same video multiple times, the library just downloads it over and over, instead of caching and reusing the first downloaded file.
To save some bandwidth, we can cache request results and keep them for some time. But it may be impossible to put such code directly into the service class because it is part of third party library of which we don't have the source code. That's why we put the caching code into a new proxy class which implements the same interface as the service class. It delegates to the service object only when the real requests have to be sent.
The structure of the solution is shown in the figure below.
领英推荐
The "TED-TalksDownloader" class is our own class that we use within the client application to manage TED videos. TED-TalksDownloader works directly with a service object and stays unchanged as long as it works with the service object through an interface. We can safely pass a proxy object instead of a real service object since they both implement the same interface. The application can also configure proxies on the fly.
Below we see the code that we could find in the third-party library TED. We have the interface definition "TEDTalksLib" and the service class "TEDTalksService" that provides us with two methods, one to obtain the titles list of the most popular video and the other to download the video by its title.
I put only the code conceptually relevant to our example, simulating the connection and download operations with random times. In the same library we also find the interface and data classes (the "Video" class).
In the code below we see the code of the class that we use to access the services of the TED library TEDTalksDownloader and we also see the Proxy class "TEDTalksProxy" which implements the caching mechanisms.
The proxy object has the same interface as a service, which makes it interchangeable with a real object when passed to a client. So, we can pass either third-party service class or proxy to the our utility class TEDTalksDownloader.
Below we see the test program that performs the following operations: it downloads the list of the most popular videos and repeats the download of all the videos in the list twice.
These operations are performed first using the third-party service class directly (TEDTalksService) and then instead using the caching proxy we made (TEDTalksProxy).
As we can see from the console output below, we have halved the bandwidth consumption because with the proxy the second download cycle of the catalogue videos is fetched from the cache.
The last simple conceptual example is the one of a ParentalControl Application, a very simple real-life scenario which prevents access to some sites that could be dangerous (or otherwise inappropriate) for children. The restriction takes place on the basis of some rules which control the presence of words within the url string. The proxy first checks the url you are connecting to, if it passes the restriction rules, then it connects to the real internet.
Let's see below the code of this simple parental control application (figure 12). We clearly see the classic structure of the proxy pattern where the proxy class checks the url before passing the request to the real connection class. If the check is not passed then an "Access Denied" exception is asserted.
Lets see how it works : As one of the site contains a word in the banned Map wards, so running the program will give the output :
it works fine.
After analyzing the structure of the proxy pattern and after examining some examples?let's summarize the steps to implement it:
And finally let us recall the main advantages of this pattern:
Before concluding I would like to share with you my thoughts on the use of patterns.
Knowing the patterns and knowing how to apply them is very important because they almost always allow us to solve problems with the right structures. In my opinion, however, we must not be excessively rigorous when we apply them but we must adapt them to the context and let us to be drive by our experience and imagination. What is important to understand about the patterns is the spirit and the semantics behind them and not so much the class diagram that we find in the Gang Of Four catalogue (.1).
For this purpose, in the next article I want to show you the unconventional use of the Proxy pattern in an Android environment. We will develop an extension of the Android SDKs to easily and naturally access a remote key-value dictionary.
In the first example we will use the Content Provider technology and in the second example we will use an object present in the Linux Kernel, the "Shared Memory". In both examples we will see how the application of the proxy pattern will make it easier the usage because it will create a wrapper to cover the main object’s complexity from the client.
I remind you my newsletter?"Sw Design & Clean Architecture":??https://lnkd.in/eUzYBuEX ?where you can find my previous articles and where you can register, if you have not already done, so you will be notified when I publish new articles.
thanks for reading my article, and I hope you have found the topic useful,
Feel free to leave any feedback.
Your feedback is very appreciated.
?Thanks again.
Stefano
?
?
References:
1.?Gamma, Helm, Johnson, Vlissides, “Design Patterns”, Addison Wesley (2° Edition October 2002). pp 207-217.
2. Alexander Shvets, "Dive Into Design Patterns", Refactoring.Guru, 2021.
3.?S. Santilli: "https://www.dhirubhai.net/pulse/open-closed-principle-stefano-santilli/".?