{
  "$type": "site.standard.document",
  "canonicalUrl": "https://rednafi.com/python/proxy-pattern/",
  "description": "Learn the proxy design pattern in Python to add access control, caching, and validation layers without modifying core functionality.",
  "path": "/python/proxy-pattern/",
  "publishedAt": "2020-06-16T00:00:00.000Z",
  "site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
  "tags": [
    "Python",
    "Design Patterns"
  ],
  "textContent": "In Python, there's a saying that \"design patterns are anti-patterns\". Also, in the realm of\ndynamic languages, design patterns have the notoriety of injecting additional abstraction\nlayers to the core logic and making the flow gratuitously obscure. Python's dynamic nature\nand the treatment of functions as first-class objects often make Java-ish design patterns\nredundant.\n\nInstead of littering your code with seemingly over-engineered patterns, you can almost\nalways take the advantage of Python's first-class objects, duck-typing, monkey-patching etc\nto accomplish the task at hand. However, recently there is one design pattern that I find\nmyself using over and over again to write more maintainable code and that is the Proxy\npattern. So I thought I'd document it here for future reference.\n\nThe proxy pattern\n\nBefore diving into the academic definition, let's try to understand the Proxy pattern from\nan example.\n\nHave you ever used an access card to go through a door? There are multiple options to open\nthat door i.e. it can be opened either using access card or by pressing a button that\nbypasses the security. The door's main functionality is to open but there is a proxy added\non top of it to add some functionality. Let me better explain it using the code example\nbelow:\n\nThe above code snippet concretizes the example given before. Here, the Door class has a\nsingle method called open_method which denotes the action of _opening_ on the Door\nobject. This method gets extended in the SecuredDoor class and in this case, I've just\nadded a print statement to the method of the latter class.\n\nNotice how the class Door was called from SecuredDoor via [composition]. In the case of\nproxy pattern, you can substitute primary object with the proxy object without any\nadditional changes in the code. This conforms to the [Liskov Substitution Principle]. It\nstates:\n\n> Objects of a superclass shall be replaceable with objects of its subclasses without\n> breaking the application. That requires the objects of your subclasses to behave in the\n> same way as the objects of your superclass.\n\nThe Door object can be replaced by the SecuredDoor and the SecuredDoor class doesn't\nintroduce any new methods, it only extends the functionality of the open_method of the\nDoor class.\n\nIn plain words:\n\n> Using the proxy pattern, a class represents the functionality of another class.\n\nWikipedia says:\n\n> A proxy, in its most general form, is a class functioning as an interface to something\n> else. A proxy is a wrapper or agent object that is being called by the client to access\n> the real serving object behind the scenes. Use of the proxy can simply be forwarding to\n> the real object, or can provide additional logic. In the proxy extra functionality can be\n> provided, for example caching when operations on the real object are resource intensive,\n> or checking preconditions before operations on the real object are invoked.\n\nPedagogically, the proxy pattern belongs to a family of patterns called the [structural\npattern].\n\nWhy use it?\n\nLoose coupling\n\nProxy pattern lets you easily decouple your core logic from the added functionalities that\nmight be needed on top of that. The modular nature of the code makes maintaining and\nextending the functionalities of your primary logic a lot quicker and easier.\n\nSuppose, you're defining a division function that takes two integers as arguments and\nreturns the result of the division between them. It also handles edge cases like\nZeroDivisionError or TypeError and logs them properly.\n\nYou can see this function is already doing three things at once which violates the [Single\nResponsibility Principle]. SRP says that a function or class should have only one reason to\nchange. In this case, a change in any of the three responsibilities can force the function\nto change. Also this means, changing or extending the function can be difficult to keep\ntrack of.\n\nInstead, you can write two classes. The primary class Division will only implement the\ncore logic while another class ProxyDivision will extend the functionality of Division\nby adding exception handlers and loggers.\n\nIn the example above, since both Division and ProxyDivision class implement the same\ninterface, you can swap out the Division class with ProxyDivision and vice versa. The\nsecond class neither inherits directly from the first class nor it adds any new method to\nit. This means you can easily write another class to extend the functionalities of\nDivision or DivisionProxy class without touching their internal logics directly.\n\nEnhanced testability\n\nAnother great advantage of using the proxy pattern is enhanced testability. Since your core\nlogic is loosely coupled with the extended functionalities, you can test them out\nseparately. This makes the test more succinct and modular. It's easy to demonstrate the\nbenefits with our previously mentioned Division and ProxyDivision classes. Here, the\nlogic of the primary class is easy to follow and since this class only holds the core logic,\nit's crucial to write unit test for this before testing the added functionalities.\n\nTesting out the Division class is much cleaner than testing the previously defined\ndivision function that tries to do multiple things at once. Once you're done testing the\nprimary class, you can proceed with the additional functionalities. Usually, this decoupling\nof core logic from the cruft and the encapsulation of additional functionalities result in\nmore reliable and rigorous unit tests.\n\nProxy pattern with interface\n\nIn the real world, your class won't look like the simple Division class having only a\nsingle method. Usually your primary class will have multiple methods and they will carry out\nmultiple sophisticated tasks. By now, you probably have grasped the fact that the proxy\nclasses need to implement all of the methods of the primary class. While writing a proxy\nclass for a complicated primary class, the author of that class might forget to implement\nall the methods of the primary class. This will lead to a violation of the proxy pattern.\nAlso, it can be hard to follow all the methods of the primary class if the class is large\nand complicated.\n\nHere, the solution is an interface that can signal the author of the proxy class about all\nthe methods that need to be implemented. An _interface_ is nothing but an abstract class\nthat dictates all the methods a concrete class needs to implement. However, interfaces can't\nbe initialized independently. You'll have to make a subclass of the interface and implement\nall the methods defined there. Your subclass will raise error if it fails to implement any\nof the methods of the interface. Let's look at a minimal example of how you can write an\ninterface using Python's abc.ABC and abc.abstractmethod and achieve proxy pattern with\nthat.\n\nIt's evident from the above workflow that you'll need to define an Interface class first.\nPython provides abstract base classes as ABC in the abc module. Abstract class\nInterface inherits from ABC and defines all the methods that the concrete class will\nhave to implement later. Concrete class inherits from the interface and implements all the\nmethods defined in it. Notice how each method in the Interface class is decorated with the\n@abstractmethod decorator. The @abstractmethod decorator turns a normal method into an\nabstract method which means that the method is nothing but a blueprint of the required\nmethods that the concrete subclass will have to implement later. If your knowledge on\ndecorators is fuzzy, check out my [Python decorators] post. You can't directly instantiate\nInterface or use any of the abstract methods without making subclasses of the interface\nand implementing the methods.\n\nThe second class Concrete is the actual class that inherits from the abstract base class\n(interface) Interface and implements all the methods mentioned as abstract methods. This\nis a real class that you can instantiate and the methods can be used directly. However, if\nyou forget to implement any of the abstract methods defined in the Interface then you'll\ninvoke TypeError.\n\nThe third class Proxy extends the functionalities of the base concrete class Concrete.\nIt calls the Concrete class using the composition pattern and implements all the methods.\nHowever, in this case, I used the results from the concrete methods and extended their\nfunctionalities without code duplication.\n\nAnother practical example\n\nLet's play around with one last real-world example to concretize the concept. Suppose, you\nwant to collect data from an external API endpoint. To do so, you hit the endpoint with\nGET requests from your HTTP client and collect the responses in json format. Then say,\nyou also want to inspect the response header and the arguments that were passed while\nmaking the request.\n\nNow, in the real world, public APIs will often impose rate limits and when you go over the\nlimit with multiple get requests, your client will likely throw an http connection-timeout\nerror. Say, you want to handle this exceptions outside of the core logic that will send the\nHTTP GET requests.\n\nAgain, let's say you also want to cache the responses if the client has seen the arguments\nin the requests before. This means, when you send requests with the same arguments multiple\ntimes, instead of hitting the APIs with redundant requests, the client will show you the\nresponses from the cache. Caching improves API response time dramatically.\n\nFor this demonstration, I'll be using Postman's publicly available GET API.\n\nThis API is perfect for the demonstration since it has a rate limiter that kicks in\narbitrarily and make the client throw ConnectTimeOut and ReadTimeOutError. See how this\nworkflow is going to look like:\n\n- Define an interface called IFetchUrl that will implement three abstract methods. The\n  first method get_data will fetch data from the URL and serialize them into json\n  format. The second method get_headers will probe the data and return the header as a\n  dictionary. The third method get_args will also probe the data like the second method\n  but this time it will return the query arguments as a dictionary. However, in the\n  interface, you won't be implementing anything inside the methods.\n\n- Make a concrete class named FetchUrl that will derive from interface IFetchUrl. This\n  time you'll implement all three methods defined in the abstract class. However, you\n  shouldn't handle any edge cases here. The method should contain pure logic flow without\n  any extra fluff.\n\n- Make a proxy class called ExcFetchUrl. It will also inherit from the interface but this\n  time you'll add your exception handling logics here. This class also adds logging\n  functionality to all the methods. Here you call the concrete class FetchUrl in a\n  composition format and avoid code repetition by using the methods that's been already\n  implemented in the concrete class. Like the FetchUrl class, here too, you've to\n  implement all the methods found in the abstract class.\n\n- The fourth and the final class will extend the ExcFetchUrl and add caching functionality\n  to the get_data method. It will follow the same pattern as the ExcFetchUrl class.\n\nSince, by now, you're already familiar with the workflow of the proxy pattern, let's dump\nthe entire 110 line solution all at once.\n\nIn the get_data method of the FetchUrl class, I've used [HTTPx] to fetch the data from\nthe URL. Pay attention to the fact that I've practically ignored all the additional logics\nof error handling and logging here. The exception handling and logging logic were added via\nExcFetchUrl proxy class. Another class CacheFetchUrl further extends the proxy class\nExcFetchUrl by adding cache functionality to the get_data method.\n\nIn the main section, you can use any of the FetchUrl, ExcFetchUrl or CacheFetchUrl\nwithout any additional changes to the logic of these classes. The FetchUrl is the barebone\nclass that will fail in case of the occurrence of any exceptions. The latter classes appends\nadditional functionalities while maintaining the same interface.\n\nThe output basically prints out the results returned by the get_headers and get_args\nmethods. Also notice, how I picked the endpoint arguments to simulate caching. The\nCache Info: on the third line of the output shows when data is served from the cache.\nHere, hits=0 means data is served directly from the external API. However, if you inspect\nthe later outputs, you'll see when the query arguments get repeated ([1, 2, 3, 1, 2, 3]),\nCache Info: will show higher hit counts. This means that the data is being served from the\ncache.\n\nShould you use it?\n\nWell, yes obviously. But not always. You see, you need a little bit of planning before\norchestrating declarative solution with the proxy pattern. It's not viable to write code in\nthis manner in a throwaway script that you don't have to maintain in the long run. Also,\nthis OOP-cursed additional layers of abstraction can make your code subjectively unreadable.\nSo use the pattern wisely. On the flip side, proxy pattern can come in handy when you need\nto extend the functionality of some class arbitrarily as it can work as a gateway to the El\nDorado of loose coupling.\n\nFurther reading\n\n- [Proxy pattern]\n- [Design patterns for humans - proxy pattern]\n\n\n\n\n[composition]:\n    https://realpython.com/inheritance-composition-python/#composition-in-python\n\n[liskov substitution principle]:\n    https://en.wikipedia.org/wiki/Liskov_substitution_principle\n\n[structural pattern]:\n    https://en.wikipedia.org/wiki/Structural_pattern\n\n[single responsibility principle]:\n    https://stackify.com/solid-design-principles/\n\n[python decorators]:\n    /python/decorators\n\n[httpx]:\n    https://github.com/encode/httpx\n\n[proxy pattern]:\n    https://github.com/faif/python-patterns/blob/d4b7f97b3ac07cb545caca32826186e32ec1d88a/patterns/structural/proxy.py\n\n[design patterns for humans - proxy pattern]:\n    https://github.com/kamranahmedse/design-patterns-for-humans#-proxy",
  "title": "Implementing proxy pattern in Python"
}