He escuchado algunas voces que dicen que verificar un valor nulo devuelto de los métodos es un mal diseño. Me gustaría escuchar algunas razones para esto.
pseudocódigo:
variable x = object.method()
if (x is null) do something
He escuchado algunas voces que dicen que verificar un valor nulo devuelto de los métodos es un mal diseño. Me gustaría escuchar algunas razones para esto.
pseudocódigo:
variable x = object.method()
if (x is null) do something
Respuestas:
La razón detrás de no devolver nulo es que no tiene que verificarlo y, por lo tanto, su código no necesita seguir una ruta diferente en función del valor de retorno. Es posible que desee consultar el Patrón de objetos nulos que proporciona más información al respecto.
Por ejemplo, si tuviera que definir un método en Java que devolviera una Colección, normalmente preferiría devolver una colección vacía (es decir Collections.emptyList()
) en lugar de nula, ya que significa que mi código de cliente está más limpio; p.ej
Collection<? extends Item> c = getItems(); // Will never return null.
for (Item item : c) { // Will not enter the loop if c is empty.
// Process item.
}
... que es más limpio que:
Collection<? extends Item> c = getItems(); // Could potentially return null.
// Two possible code paths now so harder to test.
if (c != null) {
for (Item item : c) {
// Process item.
}
}
null
es un "contenedor", que contiene cero o un puntero a los objetos. (Así es como es modelado explícitamente por Haskell's Maybe
en esos casos, y es mucho mejor por hacerlo.)
Aquí está la razón.
En Clean Code de Robert Martin , escribe que devolver nulo es un mal diseño cuando en su lugar puede devolver, digamos, una matriz vacía. Dado que el resultado esperado es una matriz, ¿por qué no? Le permitirá iterar sobre el resultado sin condiciones adicionales. Si es un número entero, tal vez sea suficiente 0, si es un hash, un hash vacío. etc.
La premisa es no forzar el código de llamada para manejar inmediatamente los problemas. Es posible que el código de llamada no quiera preocuparse por ellos. Por eso, en muchos casos, las excepciones son mejores que nulas.
Buenos usos de devolver nulo:
Malos usos: tratar de reemplazar u ocultar situaciones excepcionales como:
En esos casos, lanzar una excepción es más adecuado ya que:
Sin embargo, las Excepciones no deben usarse para manejar condiciones normales de operación del programa tales como:
boolean login(String,String)
parece estar bien y también lo haceAuthenticationContext createAuthContext(String,String) throws AuthenticationException
Sí, devolver NULL es un diseño terrible , en un mundo orientado a objetos. En pocas palabras, el uso NULL conduce a:
Consulte esta publicación de blog para obtener una explicación detallada: http://www.yegor256.com/2014/05/13/why-null-is-bad.html . Más en mi libro Elegant Objects , Sección 4.1.
bool TryGetBlah(out blah)
or FirstOrNull()
o MatchOrFallback(T fallbackValue)
.
isEmpty()
) y solo si es verdadero, llama al método. Las personas argumentan en contra de que el segundo dice que es un rendimiento inferior, pero como dice la filosofía de Unix, "valorar el tiempo humano sobre el tiempo de la máquina" (es decir, el rendimiento trivialmente más lento desperdicia menos tiempo que los desarrolladores que depuran el código que está dando errores espurios).
¿Quién dice que este es un mal diseño?
La comprobación de nulos es una práctica común, incluso recomendada, de lo contrario corre el riesgo de NullReferenceExceptions en todas partes. Es mejor manejar el error con gracia que lanzar excepciones cuando no es necesario.
Según lo que has dicho hasta ahora, creo que no hay suficiente información.
Devolver nulo desde un método CreateWidget () parece malo.
Devolver nulo de un método FindFooInBar () parece estar bien.
Create...
devuelve una nueva instancia o arroja ; Get...
devuelve una instancia existente esperada o arroja ; GetOrCreate...
devuelve una instancia existente, o una nueva instancia si no existe, o arroja ; Find...
devuelve una instancia existente, si existe, onull
. Para consultas de colección: Get...
siempre devuelve una colección, que está vacía si no se encuentran elementos coincidentes.
Su inventor dice que es un error de mil millones de dólares!
Depende del idioma que estés usando. Si está en un lenguaje como C # donde la forma idiomática de indicar la falta de un valor es devolver nulo, entonces devolver nulo es un buen diseño si no tiene un valor. Alternativamente, en idiomas como Haskell que usan idiomáticamente la mónada tal vez para este caso, entonces devolver nulo sería un mal diseño (si fuera posible).
null
lenguajes como C # y Java a menudo se sobrecargan y se les da algún significado en el dominio. Si busca la null
palabra clave en las especificaciones del idioma, simplemente significa "un puntero no válido". Eso probablemente no significa nada en ningún dominio problemático.
null
o "no inicializado"null
Si lee todas las respuestas, queda claro que la respuesta a esta pregunta depende del tipo de método.
En primer lugar, cuando ocurre algo excepcional (IOproblem, etc.), lógicamente se lanzan excepciones. Cuando exactamente algo es excepcional es probablemente algo para un tema diferente.
Cuando se espera que un método posiblemente no tenga resultados, hay dos categorías:
Hasta que tengamos una manera oficial de denotar que una función puede o no devolver nulo, trato de tener una convención de nomenclatura para denotarlo.
Al igual que tiene la convención Try Something () para los métodos que se espera que fallen, a menudo llamo a mis métodos Safe Something () cuando el método devuelve un resultado neutral en lugar de nulo.
Todavía no estoy completamente de acuerdo con el nombre, pero no se me ocurrió nada mejor. Así que estoy corriendo con eso por ahora.
Tengo una convención en esta área que me sirvió bien
Para consultas de un solo artículo:
Create...
devuelve una nueva instancia o arrojaGet...
devuelve una instancia existente esperada o arrojaGetOrCreate...
devuelve una instancia existente, o una nueva instancia si no existe, o arrojaFind...
devuelve una instancia existente, si existe, onull
Para consultas de recopilación:
Get...
siempre devuelve una colección, que está vacía si no se encuentran elementos coincidentes [1][1] dados algunos criterios, explícitos o implícitos, dados en el nombre de la función o como parámetros.
Get
cuando espero que esté allí, de modo que si no está allí, entonces es un error y arrojo, nunca necesito verificar el valor de retorno. Lo uso Find
si realmente no sé si está ahí o no, entonces necesito verificar el valor de retorno.
Las excepciones son por circunstancias excepcionales .
Si su función está destinada a encontrar un atributo asociado con un objeto dado, y ese objeto no tiene dicho atributo, puede ser apropiado devolver nulo. Si el objeto no existe, lanzar una excepción puede ser más apropiado. Si la función está destinada a devolver una lista de atributos, y no hay ninguno a devolver, tiene sentido devolver una lista vacía: está devolviendo todos los atributos cero.
Está bien devolver nulo si hacerlo es significativo de alguna manera:
public String getEmployeeName(int id){ ..}
En un caso como este, es significativo devolver nulo si la identificación no corresponde a una entidad existente, ya que le permite distinguir el caso en el que no se encontró coincidencia de un error legítimo.
La gente puede pensar que esto es malo porque se puede abusar de él como un valor de retorno "especial" que indica una condición de error, que no es tan buena, un poco como devolver códigos de error de una función pero confuso porque el usuario tiene que verificar el retorno para nulo, en lugar de detectar las excepciones apropiadas, p. ej.
public Integer getId(...){
try{ ... ; return id; }
catch(Exception e){ return null;}
}
No es necesariamente un mal diseño, ya que con tantas decisiones de diseño, depende.
Si el resultado del método es algo que no tendría un buen resultado en el uso normal, devolver nulo está bien:
object x = GetObjectFromCache(); // return null if it's not in the cache
Si realmente siempre debería haber un resultado no nulo, entonces sería mejor lanzar una excepción:
try {
Controller c = GetController(); // the controller object is central to
// the application. If we don't get one,
// we're fubar
// it's likely that it's OK to not have the try/catch since you won't
// be able to really handle the problem here
}
catch /* ... */ {
}
Optional<>
Para ciertos escenarios, desea notar una falla tan pronto como suceda.
Verificar contra NULL y no afirmar (para errores del programador) o arrojar (para errores del usuario o de la persona que llama) en el caso de falla puede significar que los bloqueos posteriores son más difíciles de rastrear, porque no se encontró el caso impar original.
Además, ignorar los errores puede conducir a vulnerabilidades de seguridad. Quizás la nulidad vino del hecho de que se sobreescribió un búfer o similar. Ahora, usted está no estrellarse, lo que significa que el explotador tiene la oportunidad de ejecutar en el código.
¿Qué alternativas ves para volver nulo?
Veo dos casos:
En este caso podríamos: Devolver nulo o lanzar una excepción (marcada) (o tal vez crear un artículo y devolverlo)
En este caso, podríamos devolver Null, devolver una lista vacía o lanzar una excepción.
Creo que el retorno nulo puede ser menos bueno que las alternativas porque requiere que el cliente recuerde verificar si hay nulo, los programadores olvidan y codifican
x = find();
x.getField(); // bang null pointer exception
En Java, lanzar una excepción marcada, RecordNotFoundException, permite que el compilador le recuerde al cliente que trate con el caso.
Creo que las búsquedas que devuelven listas vacías pueden ser bastante convenientes: simplemente complete la pantalla con todos los contenidos de la lista, oh, está vacía, el código "simplemente funciona".
A veces, devolver NULL es lo correcto, pero específicamente cuando se trata de secuencias de diferentes tipos (matrices, listas, cadenas, lo que tienes), probablemente sea mejor devolver una secuencia de longitud cero, ya que conduce a un código más corto y, con suerte, más comprensible, sin tener que escribir mucho más por parte del implementador de la API.
Haga que llamen a otro método después del hecho para averiguar si la llamada anterior fue nula. ;-) Hey, fue lo suficientemente bueno para JDBC
La idea básica detrás de este hilo es programar a la defensiva. Es decir, código contra lo inesperado. Hay una variedad de respuestas diferentes:
Adamski sugiere mirar el Patrón de Objetos Nulos, con esa respuesta votada por esa sugerencia.
Michael Valenty también sugiere una convención de nombres para decirle al desarrollador lo que se puede esperar. ZeroConcept sugiere un uso adecuado de Exception, si ese es el motivo del NULL. Y otros.
Si hacemos la "regla" de que siempre queremos hacer una programación defensiva, entonces podemos ver que estas sugerencias son válidas.
Pero tenemos 2 escenarios de desarrollo.
Clases "autorizadas" por un desarrollador: El autor
Clases "consumidas" por otro (quizás) desarrollador: el Desarrollador
Independientemente de si una clase devuelve NULL para métodos con un valor de retorno o no, el desarrollador deberá probar si el objeto es válido.
Si el desarrollador no puede hacer esto, entonces esa Clase / método no es determinista. Es decir, si la "llamada al método" para obtener el objeto no hace lo que "anuncia" (por ejemplo, getEmployee), ha roto el contrato.
Como autor de una clase, siempre quiero ser tan amable y defensivo (y determinista) al crear un método.
Entonces, dado que NULL o NULL OBJECT (p. Ej., Si (employee como NullEmployee.ISVALID)) deben verificarse y eso puede suceder con una colección de Empleados, entonces el enfoque de objeto nulo es el mejor enfoque.
Pero también me gusta la sugerencia de Michael Valenty de nombrar el método que DEBE devolver nulo, por ejemplo, getEmployeeOrNull.
Un autor que lanza una excepción está eliminando la opción para que el desarrollador pruebe la validez del objeto, lo cual es muy malo en una colección de objetos, y obliga al desarrollador a manejar excepciones al bifurcar su código de consumo.
Como desarrollador que consume la clase, espero que el autor me brinde la capacidad de evitar o programar la situación nula a la que se enfrentan sus clases / métodos.
Entonces, como desarrollador, programaría defensivamente contra NULL desde un método. Si el autor me ha dado un contrato que siempre devuelve un objeto (el OBJETO NULO siempre lo hace) y ese objeto tiene un método / propiedad por el cual probar la validez del objeto, entonces usaría ese método / propiedad para continuar usando el objeto de lo contrario, el objeto no es válido y no puedo usarlo.
La conclusión es que el Autor de la Clase / Métodos debe proporcionar mecanismos que un Desarrollador pueda usar en su programación defensiva. Es decir, una intención más clara del método.
El desarrollador siempre debe usar programación defensiva para probar la validez de los objetos devueltos por otra clase / método.
Saludos
GregJF
Otras opciones para esto son: devolver algún valor que indique éxito o no (o tipo de error), pero si solo necesita un valor booleano que indicará éxito / falla, devolverá nulo para falla y un objeto para éxito no ser menos correcto, luego devolver verdadero / falso y obtener el objeto a través del parámetro.
Otro enfoque sería utilizar excepciones para indicar fallas, pero aquí, en realidad, hay muchas más voces que dicen que esta es una práctica MALA (ya que el uso de excepciones puede ser conveniente pero tiene muchas desventajas).
Por lo tanto, personalmente no veo nada malo en devolver nulo como indicación de que algo salió mal y verificarlo más tarde (para saber realmente si ha tenido éxito o no). Además, pensar ciegamente que su método no devolverá NULL y luego basará su código en él, puede dar lugar a otros errores, a veces difíciles de encontrar (aunque en la mayoría de los casos simplemente bloqueará su sistema :), como hará referencia a 0x00000000 tarde o temprano).
Las funciones nulas no intencionadas pueden surgir durante el desarrollo de programas complejos y, como el código muerto, tales ocurrencias indican fallas graves en las estructuras de los programas.
Una función o método nulo se usa a menudo como el comportamiento predeterminado de una función revectable o método reemplazable en un marco de objeto.
Bueno, de seguro depende del propósito del método ... A veces, una mejor opción sería lanzar una excepción. Todo depende de un caso a otro.
Si el código es algo como:
command = get_something_to_do()
if command: # if not Null
command.execute()
Si tiene un objeto ficticio cuyo método execute () no hace nada, y devuelve eso en lugar de Nulo en los casos apropiados, no tiene que verificar el caso Nulo y, en su lugar, solo puede hacer:
get_something_to_do().execute()
Entonces, aquí el problema no es entre verificar NULL frente a una excepción, sino entre que la persona que llama tiene que manejar los casos especiales de manera diferente (de cualquier manera) o no.
Para mi caso de uso, necesitaba devolver un Mapa del método y luego buscar una clave específica. Pero si devuelvo un mapa vacío, conducirá a NullPointerException y no será muy diferente devolver nulo en lugar de un mapa vacío. Pero a partir de Java8 en adelante podríamos usar Opcional . Lo anterior es la razón por la cual se introdujo el concepto opcional.
G'day
Devolver NULL cuando no puede crear un nuevo objeto es una práctica estándar para muchas API.
¿Por qué demonios es un mal diseño? No tengo idea.
Editar: Esto es cierto para los lenguajes en los que no tiene excepciones, como C, donde ha sido la convención durante muchos años.
HTH
'Avahappy,