Is your Python function signature starting to look like a grocery list? In my latest video, I show how to reduce the number of arguments you pass around by using the Context Object Pattern. ✅ Cleaner APIs ✅ Easier testing ✅ Fewer bugs But... there's a catch. As I show in the video, this pattern can also make things worse if you use it the wrong way. 📺 Watch here → https://lnkd.in/eH5GpRW2 #Python #CleanCode #SoftwareDesign #ArjanCodes #DesignPatterns
You can take it a step further by applying the functional builder pattern. Instead of a record you provide a lambda from a record builder to a record builder. This allows the function to set defaults which can be overwritten. by the caller. Using the builder you simulate named parameters. A slightly improved version would have the lambda return an object that only has a build method. This prevents the function to making changes to the values set by the user. Making the builder to be buildable once also helps in preventing a shared state between calls. I am a Java developer. Java has neither default or named parameters. The functional builder is one way to simulate those. Another way is using annotations (attributes for the .NET developer). A runtime system then fills in defaults based on annotations. The disadvantages of the functional builder pattern ( in Java) are: 1. defaults are not part of the method signature 2. The method signature itself is something you need to get used to Advantage over just a record: 1. Cleaner when calling. 2 Only provide what is required in one go. 3. No need to create a record first
In Hylo and Scala you can use implicit parameters for such dependency injection purposes. Take care not to weaken the invariants of the AppContext, don't have optional or partially initialized state within that because it will make reasoning about what a function expects very hard.
I don't like general variable names like context, data or something. It looks similar to God Object antipattern but for information storing and is violation of "explicit is better than implicit" Zen
It shows how poorly Python is designed when you have to pass contextual parameters together with business ones in the same argument list. Moreover, every function signature in the call chain must be polluted with the context parameter, even if intermediate layers don’t use it directly. Calling this "cleaner APIs" is misleading, it’s a way of papering over fundamentally poor design decisions. A proper solution would rely on type classes or reader/environment monads, or at least encapsulate the context in a thread-local mechanism.
How about single responsibility? render_article takes so many different items and AppContext (I would have named it to ApplicationContext) is way to scattered, like the complete application is passed around. Passing context items is good but it should focus and not contain everything.
I am always surprised when looking at those lists of parameters. The only reason why they exist is, probably, that typically there are more that one context. Often you have several contexts organised hierarchically. There are many ways to solve that problem, from inheritance to just including one context into another. But it always requires some effort to structure those contexts. In practice, that often results in several refactorings to get something reasonable. Lazy people skip that.)