Following my post on the Special Case pattern, I just wanted to give a few more examples in which the pattern proves to be really helpful. In the previous post, we talked about using the Special Case pattern when returning collections, but you can use it in other places as well.
Subclassing
Suppose we have a cached collection of User objects, and we want to fetch a specific User and save it to the database.
User user = cache.Get(”John”);
if (user != null)
{
user.Save();
}
If we subclass User with a class called MissingUser, and have our cache object return it when it can’t find a user, we can reduce our calling code to:
User user = cache.Get(”John”);
user.Save();
MissingUser.Save() overrides User.Save() and does nothing.
Side note: we could definitely go with checking cache.Contains(”John”), and acting on the result, but I think this is too much detail for this level of abstraction, and may be violating Tell, Don’t Ask.
Instantiation
Suppose we want to display a User’s profile. Our User class has a string called Name and a bitmap called Picture.
User user = profiles.Get(”Mark”);
if (user != null)
{
this.Name = user.Name; // Binding is for the weak.
this.Picture = user.Picture;
}
When a User is not found in the profiles repository, we could make it return an instance of the regular User class, where User.Name = “Not Found”, and Picture is something like
![]()
This way, we can just write:
User user = profiles.Get(”Mark”);
this.Name = user.Name;
this.Picture = user.Picture;
and a profile will always be displayed.
Conditioning on Instantiation
What if we wanted to check if a User exists in a managers repository, and only if it is not found, fetch it from a clerks repository? One might be tempted to write something like:
User user = managers.Get(”Ahmed”);
if (user != null)
{
// Ahmed is a manager.
Display(user);
}
else
{
// Ahmed must be a clerk.
user = clerks.Get(”Ahmed”);
Display(user);
}
In the previous examples, we avoided the conditional (null check) by using Special Case. If you look at the requirement for this example, you’ll see that the conditional is built into it, and there is no way to get around it.
But if we use the same technique we used in the second example, we could create an instance of User, that has a boolean property called Exists, which will be populated according to the result. This way, we could express our condition much better:
User user = managers.Get(”Ahmed”);
if (user.Exists)
{
// Ahmed is a manager.
Display(user);
}
else
{
// Ahmed must be a clerk.
user = clerks.Get(”Ahmed”);
Display(user);
}
No code saved here, but our intention is much clearer this way.
What the hell is a “if (user != null)”? English, people.
One advantage of returning null is the null coalescing operator:
User user = managers.Get(”Ahmed”) ?? clerks.Get(”Ahmed”);
or
User user = profiles.Get(”Mark”) ?? new User();
[…] rauchy’s Blog - Desperately Trying to Decouple www.rauchy.net/blog/2009/12/special-case-it-part-two – view page – cached Following my post on the Special Case pattern, I just wanted to give a few more examples in which the pattern proves to be really helpful. In the previous post, we talked about using the Special Case pattern when returning collections, but you can use it in other places as well. […]
Gavin,
Indeed, the null coalescing operator can help reduce the number of lines of code, but that’s not always a positive thing. I like to keep my code verbal, and things like the null coalescing opeartor or the ternary operator can cause a cognitive break IMO.
Besides, if you are returning nulls, you can’t tell if your users will use the null coalescing operator or will just use null checks. If you are developing a framework, I say let your users decide how they handle nulls, but if you are working on code that is only available to teammates, provide some guidance.
Something to consider:
Although I hate checking for nulls (and even more to return nulls… yuck), sometimes it’s part of the common sense logic,
and avoiding doing so can confuse the innocent reader of your code, even though that’s exactly what you were trying not to do.
Maybe such is in this case, I would ask myself: “Hey, and what if there’s no user named John? does he he save it?!
How does he show Mark’s profile if there’s no Mark?
Shai,
A valid point, but remember that describing your business logic using null checks is a poor choice.
There was a similar discussion going on in another blog, thus I can make this short and simply refer you to it:
http://www.thehackerchickblog.com/2008/10/just-say-no-to-nulls-or-refactoring.html