The most common way of handling cases where there is no object to return, the code just returns null instead.
The problem with this approach is that the client now has to handle two different types of results. One if an object is returned and another if nothing is returned. This either results in code like this being scattered throughout the code base:
12345~
</td>
| Car car = carRepository.RetrieveCar(carId)if(car != null){ car.Drive();}~
</td>
</tr>
</tbody></table>
Or the client doesn’t bother to handle the null and we end up with a Null Pointer/Reference exception at some stage. Neither of these solution is particularly desirable.
Scala actually has an interesting way of getting around this problem by allowing you to define an interface which informs the client that there is the potential for nothing to be returned, therefore effectively designing a contract which allows the client to deal with it appropriately. The Option[T] idea is explained about half way down the page on this post.
This can also be done in C# to an extent by making use of the nullable operator. In C# it is only useful when you want to make it clear to the client that they may get a null value instead of a primitive.
Overall, this is the simplest solution and probably also the easiest to understand. It just doesn’t result in the cleanest code.
Throw Exception
The idea here is that if there is no object to return, the code throws a custom exception which describes the reason that no object was found.
123456789~
</td>
| public Car RetrieveCar(Guid carId){ Car car = FindCarInDatabaseBy(carId); if(car == null) { throw new CarNotFoundException(); } return car;}~
</td>
</tr>
</tbody></table>
This is certainly more descriptive in telling you why nothing has been returned, but as with 'Return null' at some stage the alternate result from the method call needs to be handled. If this is being done with Java’s checked exceptions then it would either need to be handled by the method which calls RetrieveCar or bubbled up through the car. There are a variety of considerations for why you would would choose each way but that discussion is for another post.
As well as this, in theory you could end up with a method returning different Exceptions depending on the reason for the failure to return an object.
I’m not a big fan of handling state via exceptions as I think that exceptions should only be used where something exceptional has happened and I don’t think that failing to find an object can be considered exceptional in most cases.
Null Object Pattern
The null object pattern is the most helpful of the null handling strategies as far as the client is concerned. The client will now know that the object it has been returned is a null object, it will just see an object of the type requested. The devil is in the detail:
1234567891011121314151617181920~
</td>
| public class Car{ public virtual void Drive() { // Do some driving } public static Car NULL { get { return new NullCar(); } } class NullCar : Car { public override void Drive() { throw new NotImplementedException(); } }}~
</td>
</tr>
</tbody></table>
123456789~
</td>
| public Car RetrieveCar(Guid carId){ Car car = FindCarInDatabaseBy(carId); if(car == null) { return Car.NULL; } return car;}~
</td>
</tr>
</tbody></table>
This pattern effectively delays the need to handle the unexpected behaviour exhibited by the RetrieveCar method. Depending on the implementation of the NullCar we might decide to throw a NotImplementedException if a method on it is ever called. Or we can just override every method to do nothing which just hides the problem as far as I’m concerned.
These are the main ways I have come across for handling nulls. I’m sure there are others so if you know of any better ways please let me know. |
|
|
|