One of the most awkward things to deal with in code is Temporal Coupling. It leads to messy fragile code, that is difficult to maintain. What do I mean by Temporal Coupling? When you consume a class/component in your code and it requires you to do certain actions in a specific order. Lets look at a couple of examples where I commonly see this:
- When using a File class, you must first call Open(), then perform one or more actions with the File class (i.e. reading/writing), then you must remember to call the close method.
- Custom data access classes that return DataReader objects. Because a DataReader requires an active connection to the database, it becomes the caller’s responsibility to close that connection once it’s done with the DataReader.
When I’m faced with code that has temporal coupling – either code I wrote, or external code that I depend on - I almost always use lambda’s to isolate and encapsulate this temporal coupling. The easiest way to explain this pattern is to show a few examples. So let’s go through some code examples of the 2 scenarios listed above.
First an example of what the code might look like with the temporal coupling in place:
For the purposes of this sample lets assume that FileSample is a class provided by an external library (maybe the .Net framework, or maybe a 3rd party library). In this example we have a method that has the FileSample instance injected into it (presumably already setup to point at the correct file). But the FileSample class has a temporal coupling problem forcing the consumer to ensure they call Open first, do their work, then call Close to ensure that the resources are properly cleaned up.
To improve this by using Lambda’s I want to isolate the temporal coupling into one place, so that all consumers of the FileSample class no longer need to worry about it. If FileSample was code that I controller, I could do this directly inside the FileSample class. In this example I’ll assume that FileSample comes from an external library, and I’ll create a FileWrapper class that encapsulates the temporal coupling issues.
Lets look at one more example:
Here we have a DataAccessLayer (that we have the code for), that returns a DataReader object. The problem is that DataReaders require an open DB connection, so the caller of GetDataReader must always remember to call close when they are finished to close the DB connection.
In this case we control the code for DataAccessLayer, so instead of creating a wrapper we can add a new method directly to the class that doesn’t expose temporal coupling issues.
And finally the new method inside the DataAccessLayer class.
Next time you find yourself having to deal with classes/components that expose temporal coupling issues, consider using lambda’s to isolate and encapsulate that temporal coupling.