Avoid bothering your users with null values – either as value for parameters or as a return value. With null as return value callers will have to write if cascades to deal with the null values at some point. This makes code both hard to write and read. In most cases null means “empty”, “none”, “not available” or “not yet available” and exactly that should be expressed by other appropriate language constructs.
In the following example GetResult could return null which requires extra logic by the caller:
String result = GetResult();
if (result == null)
result = "prefix";
result = result.Insert(0, "prefix");
|Do provide an API comment which makes it clear what possible return values of the method are. For example: “Method never returns null”.
Generalized not returning null but something “empty” is known as the null pattern. In case a method wants to return a value which expresses “not available” and this is not considered as an error it always should return an object which implements the null pattern.
|To express empty strings, collections or an enumerable use the corresponding empty string or collection. Namely: String.Empty, new List() or other empty collections and Enumerable.Empty()
Sometimes you are in the situation where it indeed plays a role whether the return value is null or empty. For example consider a dictionary of strings as keys and values and a LookUp operation on it. The caller needs to distinguish between the meanings “key not found” and “value is empty”. The latter is expressed by returning String.Empty but then this not available anymore for the “key not found” message for which it becomes tempting to returnnull.
|When there is a semantic difference between null and “empty” consider introducing a helper structure (or use System.Tuple) to transmit both: A success indicator and in case of success the result value.
In one of my last posts I analyzed how policy based class design could look like in C#. It turned out that policy based class design can help decomposing functionality which would usually be implemented in one class into smaller parts (policies). Those can then be put together (configure) in different combinations in order to compose one class which fits exactly the user’s current needs. In this post I want to work on a policy based class which provides caching functionality for one single datum.
First I want to differentiate between 2 approaches declaring a cache of data as outdated:
- The cache does not know when its data “runs out of date” and therefore uses some heuristics or strategies to determine that. For example a common strategy to declare the cache as invalid is when its content is older than a certain timespan. A web browser could follow such a strategy when it determines whether it displays a page from its cache or reloads it from the web.
- The source of data which is being cached has access to every cache and can actively invalidate them. Consider an in-process cache of an artifact doing database inserts and queries.
The cache I want to implement is going to support the first approach for now. Its only direct operation supported from a user’s perspective is getting the cached value. Internally it has to distinguish two different cases:
- The cache is valid: return the cached value
- The cache is invalid: cached value must be renewed and returned
The description of those two cases leaves open what the definiton of “when is the cache valid or invalid?” and “how can the cache be renewed?” is. Those definitions will depend on the actual usage scenario of the cache. Some developers might need a cache which becomes invalid after a certain time, others might need a cache which expires after it has been requested for a certain amount of times. Clearly the definition of when the cache is valid or invalid must be made flexible and exchangeable when implementing the cache class. In other words: It should be implemented as a policy. The same applies when obtaining fresh data in case the cache became invalid. Is calling a delegate provided by the user enough? Should that be done in a multi threading safe way? and so on. Eventually we identify two policies: An “invalidation policy” being responsible for the validity of the cache and a “retrieval policy” taking care of getting fresh data.
With that in mind we construct a basic workflow of the cache class:
- Inform invalidation policy that the cache is requested
- If invalidation policy claims that the cache is valid return the cached value
- Otherwise run retrieval policy to get update for the cached value
- Inform invalidation policy that the cache has been updated
- Return cached value
or in code:
this.value = this.cacheValueRetrievingPolicy.GetValue();
Looking at this workflow the retrieval policy turns out to be quite simple. It supports exactly one operation called “GetValue”:
public interface ICacheValueRetrievingPolicy<T>
Regarding the invalidation policy it needs to support the following operations:
- Cache class informs the policy that the user requests the content of cache
- Cache class wants to know whether the cache is valid
- Cache class informs the policy that the cache has been renewed
public interface ICacheInvalidationPolicy
Adding all things together – the cache class and the 2 policies – we are done with the first version of the value cache. I have created a small project on CodePlex called “Byleist” which contains the complete source code. You can find it here. Please note that Byleist is a portable library project. Thus you need to install the Portable Library Tools before opening the Byleist project.