Design Patterns in Action(7)- Proxy
A series of programming design patterns illustration with examples with JavaScript/Python
Simply put, the Proxy pattern is a way to avoid direct access to an object through a substitute, aka. Proxy. The proxy object looks like the same to the client as the real subject, so they will need to have the same interface. Requests from the client are passed to the real subject through the proxy, with a bit added flavour, e.g. caching, logging, encryption, etc. So in a way, it’s a bit like a middleware.
The participants in Proxy pattern:
- Real Subject: object that the client really want.
- Proxy Subject: a proxy that implements all real subject’s interface but does something extra without the client realising that.
The implementation of Proxy pattern is pretty simple, however, there’s another native implementation in JavaScript that encapsulates the pattern with an abstract API — feel free to take a look.
So, ours example will implement Proxy for the purpose of caching. Let’s take a look at python one. Note the real object fetches the bread label asynchronously so the proxy will cache the response to accelerate the speed to return the response to the client.
import asyncio
from abc import ABCMeta, abstractmethod
class IBread(metaclass=ABCMeta):
@staticmethod
@abstractmethod
def label_bread():
pass
class Bread(IBread):
kind_match = {
"Sourdough": "Your sourdough is here!",
"Ciabatta": "Your ciabatta is here!",
"Rye bread": "Your rye bread is here!",
}
def __init__(self):
pass
async def label_bread(self, kind):
await asyncio.sleep(100)
return self.kind_match.get(kind)
class BreadProxy(IBread):
def __init__(self):
self.cache = {}
self.bread = Bread()
async def label_bread(self, kind):
if not self.cache.get(kind):
self.cache[kind] = await self.bread.label_bread(kind)
return self.cache[kind]
return self.cache[kind]
async def main():
breadProxy = BreadProxy()
await breadProxy.label_bread("Sourdough")
await breadProxy.label_bread("Sourdough")
await breadProxy.label_bread("Ciabatta")
await breadProxy.label_bread("Sourdough")
if __name__ == "__main__":
asyncio.run(main())
This is another with JavaScript:
sleep = (m) => new Promise((r) => setTimeout(r, m));
class Bread {
constructor() {}
async labelBread(kind) {
switch (kind) {
case "Sourdough":
await sleep(10);
return "Your sourdough is here!";
case "Ciabatta":
await sleep(100);
return "Your ciabatta is here!";
case "Rye bread":
await sleep(30);
return "Your rye bread is here!";
}
}
}
class BreadProxy {
constructor() {
this.cache = {};
this.bread = new Bread();
}
async labelBread(kind) {
if (this.cache[kind] == null) {
this.cache[kind] = await this.bread.labelBread(kind);
}
return this.cache[kind];
}
}
const breadProxy = new BreadProxy();
(async () => {
await breadProxy.labelBread("Sourdough");
await breadProxy.labelBread("Sourdough");
await breadProxy.labelBread("Ciabatta");
await breadProxy.labelBread("Sourdough");
})();
That’s so much of it! Happy Reading!