En mi opinión, hay una diferencia entre devolver NULL, devolver algún resultado vacío (por ejemplo, la cadena vacía o una lista vacía) y lanzar una excepción.
Normalmente tomo el siguiente enfoque. Considero una función o método llamada f (v1, ..., vn) como la aplicación de una función
f : S x T1 x ... x Tn -> T
donde S it el "estado del mundo" T1, ..., Tn son los tipos de parámetros de entrada y T es el tipo de retorno.
Primero trato de definir esta función. Si la función es parcial (es decir, hay algunos valores de entrada para los que no está definida), devuelvo NULL para señalar esto. Esto se debe a que quiero que el cálculo finalice normalmente y me diga que la función que solicité no está definida en las entradas dadas. Usar, por ejemplo, una cadena vacía como valor de retorno es ambiguo porque podría ser que la función se define en las entradas y que la cadena vacía es el resultado correcto.
Creo que la verificación adicional de un puntero NULL en el código de llamada es necesaria porque está aplicando una función parcial y es la tarea del método llamado decirle si la función no está definida para la entrada dada.
Prefiero utilizar excepciones para errores que no permiten realizar el cálculo (es decir, no fue posible encontrar ninguna respuesta).
Por ejemplo, supongamos que tengo una clase Cliente y quiero implementar un método
Customer findCustomer(String customerCode)
buscar un cliente en la base de datos de la aplicación por su código. En este método, lo haría
- Devuelve un objeto de la clase Cliente si la consulta es exitosa,
- Devuelve nulo si la consulta no encuentra ningún cliente.
- Lanza una excepción si no es posible conectarte a la base de datos.
Las comprobaciones adicionales para nulo, por ejemplo
Customer customer = findCustomer("...");
if (customer != null && customer.getOrders() > 0)
{
...
}
son parte de la semántica de lo que estoy haciendo y no solo los "omitiría" para que el código se lea mejor. No creo que sea una buena práctica simplificar la semántica del problema en cuestión solo para simplificar el código.
Por supuesto, dado que la comprobación de nulo ocurre muy a menudo es bueno si el lenguaje admite alguna sintaxis especial para ello.
También consideraría usar el patrón de objeto nulo (como lo sugiere Laf) siempre que pueda distinguir el objeto nulo de una clase de todos los demás objetos.