How to Hidden Temporal Couplings / In Kotlin and Android
--
iIt might not be obvious but structure arguments and functions in a way it makes easy to understand the order in which they should be called it is not always an easy task, specially if we are running out of time and test all green goes green.
Why should I care about this ?
Let’s consider the following,
override fun bakeOne(menu: MENU): Dish {
this.selectedMenu = menu prepareIngredients() // Prepare the "Mise en place".
prepareDough() // Prepare the dough.
prepareSauce() // Prepare the sauce.
topDough() // Top dough with the sauce.
return bake()
}
As you can see the order of the four function is important. You must prepare and get ready all the ingredients first in order to prepare the Dough and the Sauce and finish the preparation toping out the Dough with the Sauce on it, finishing with the make itself of the Pizza.
Unfortunately, this code does not enforce Temporal Coupling and as a result anyone else can come and call prepareDough() before prepareIngredients(), leading to an Exception.
A better solution would be,
override fun bakeOne(menu: MENU): Dish {
val selectedMenu = menu val ingredients = prepareIngredients(selectedMenu)
val dough = prepareDough(ingredients)
val sauce = prepareSauce(ingredients)
topDough(dough, sauce)
return bake(dough)
}
In this example we expose the temporal coupling by creating buckets so each function produce a result that the next function needs, so there is no reasonable way anyone can switch function call order, even if you try this is going to lead to a compilation error.
in Android,
There is a series of initial step you must follow in order to create a screen whether is a Fragment or an Activity. This steps may includes populate UI components with a list of items like a RecycleView or just change the text of a Button.
Let’s consider the following,
initToolbarTitle()
initBreadcrumb()
initListOfElements()
initCloseButton()
initSearchExperience()
collectDataAndSubmit()
Again this code does not enforce Temporal Coupling, and as a result anyone can come and call initSearchExperience() before initToolbarTitle() or initBreadcrumb() leading to a crash of the application or can lead to a series of issues like bug or just undesirable behaviour which do not correspond to the expected requirements.
You might think, how can we use the previous approach to solve this issue ? Well, using the exactly the previous approach could increase the complexity of the functions, and you would be absolutely right.
So to avoid increasing the level of complexity of the functions and keeping clear order in the calling of the functions we using Kotlin lambda expressions to find a better solution,
val listOfInitTaskForTheView = arrayListOf(
initToolbarTitle,
initBreadcrumb,
initListOfElements,
initCloseButton,
initSearchExperience,
collectDataAndSubmit
)
listOfInitTaskForTheView.forEach { it.invoke() }
private val initToolbarTitle = { // Do something. }
Conclusion, Temporal Couplings are often necessary, but you should not hide the coupling. In addition it is a good opportunity for making your code much more verbose.
Keep in mind that this concept is not limited to any particular language, even so, some as they offer better tools to mitigate it.
Complete code on GitHub