I won't be against the first approach (returning null
) too much, but will rename the method to clearly indicate it may return null.
Signatures like:
findCustomer(int customerId)
parse(string number)
pop(stack sequence)
are not clear. If a customer doesn't exist, is the method returning null
or throwing a CustomerNotFound
exception? If the number is "abc" and cannot be reasonably parsed, is the method returning null
or throwing InvalidFormat
exception? What happens when pop
ing an element from an empty stack?
Instead:
findCustomerIfAny(int customerId)
tryParse(string number)
lastOrNull(stack sequence)
are easier to understand.
Other options you have include:
1. out
parameters
If your language supports out
parameters, a different approach would be to have the following:
tryFindCustomer(int customerId, out customer result) returns bool
The boolean result indicates whether the customer was found or not.
Personally, I find this approach too verbose and ugly. I won't use it.
2. Tuples
If your language has tuples, then you can write:
findCustomer(int customerId) returns (bool isFound, customer match)
This is perfectly valid, and may make your code extremely explicit.
3. Value or default
There is also another interesting convention in LINQ in .NET. A method which searches for the first element of a sequence, FirstOrDefault
, returns the default value if the sequence is empty.
This means that:
[ 2, 7, 1 ].FirstOrDefault()
gives 2
, while:
[].FirstOrDefault()
gives 0
, since default(int)
is 0
. Note that:
default(customer) ↔ null
default(System.Drawing.Point) ↔ System.Drawing.Point.Empty ↔ (0, 0)
etc.
Unfortunately, you can't override default
operator, which makes this approach rather limited.
I'm not sure this last approach will help in your case, but it's worth mentioning.
4. Status within the returned value
A custom-made approach would be to integrate a status value within the class itself. For example:
// `findCustomer` always returns a non-null value.
selectedCustomer = findCustomer(customerIdFromInput)
// When the customer cannot be found, `IsEmpty` is set to `true`.
if (selectedCustomer.IsEmpty) {
throw CustomerNotFoundException;
}