design - pattern - return null c++
¿Está devolviendo el diseño nulo? (23)
He escuchado algunas voces que dicen que verificar el valor nulo 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
Las funciones nulas involuntarias pueden surgir durante el desarrollo de programas complejos, y como el código muerto, tales ocurrencias indican serios defectos en las estructuras del programa.
Una función o método nulo se usa a menudo como el comportamiento predeterminado de una función revelable o método anulable en un marco de objeto.
¡Su inventor says que es un error de mil millones de dólares!
¿Qué alternativas ves devolver nulo?
Veo dos casos:
- findAnItem (id). ¿Qué debería hacer si el artículo no se encuentra?
En este caso, podríamos: devolver nulo o lanzar una excepción (marcada) (o tal vez crear un elemento y devolverlo)
- listItemsMatching (criterios) ¿qué debería devolver si no se encuentra nada?
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 comprobar 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 recuerde al cliente que debe tratar el caso.
Encuentro que las búsquedas que devuelven listas vacías pueden ser bastante convenientes, solo llene la pantalla con todos los contenidos de la lista, oh, está vacía, el código "simplemente funciona".
¿Quién dice que esto es un mal diseño?
La comprobación de valores nulos es una práctica común, incluso alentada, de lo contrario correrá el riesgo de NullReferenceExceptions en todas partes. Es mejor manejar el error con elegancia que lanzar excepciones cuando no es necesario.
A veces, devolver NULL es lo correcto, pero específicamente cuando se trata de secuencias de diferentes tipos (matrices, listas, cadenas, etc.), es mejor devolver una secuencia de longitud cero, ya que conduce a un código más corto y, con suerte, más comprensible, mientras que no requiere mucha más escritura por parte del implementador de API.
Bueno, seguro depende del propósito del método ... A veces, una mejor opción sería lanzar una excepción. Todo depende de caso a caso.
Buenos usos de devolver nulo:
- Si null es un resultado funcional válido, por ejemplo: FindFirstObjectThatNeedsProcessing () puede devolver nulo si no se encuentra y la persona que llama debe verificar en consecuencia.
Malos usos: tratar de reemplazar u ocultar situaciones excepcionales tales como:
- atrapar (...) y devolver nulo
- La inicialización de la dependencia API falló
- Sin espacio en disco
- Los parámetros de entrada no válidos (error de programación, las entradas deben ser desinfectadas por la persona que llama)
- etc
En esos casos, arrojar una excepción es más adecuado ya que:
- Un valor de retorno nulo no proporciona información de error significativa
- La persona que llama inmediatamente no puede manejar la condición de error
- No hay garantía de que la persona que llama esté buscando resultados nulos
Sin embargo, las excepciones no se deben usar para manejar las condiciones normales de operación del programa, tales como:
- Nombre de usuario / contraseña inválidos (o cualquier entrada proporcionada por el usuario)
- Breaking loops o como gotos no locales
Depende del idioma que estés usando. Si está en un idioma 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 lenguajes como Haskell que usan idiomáticamente la mónada Maybe para este caso, entonces devolver nulo sería un mal diseño (si fuera posible).
En base a lo que has dicho hasta ahora, creo que no hay suficiente información.
La devolución nula de un método CreateWidget () parece incorrecta.
La devolución de null de un método FindFooInBar () parece estar bien.
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 el ID no se corresponde con una entidad existente, ya que le permite distinguir el caso donde no se encontró una coincidencia de un error legítimo.
La gente puede pensar que esto es malo porque puede ser abusado 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 atrapar las excepciones apropiadas, por ej.
public Integer getId(...){
try{ ... ; return id; }
catch(Exception e){ return null;}
}
Esta es la razón.
En Clean Code por Robert Martin , escribe que devolver null es un mal diseño cuando en su lugar se puede devolver, por ejemplo, una matriz vacía. Dado que el resultado esperado es una matriz, ¿por qué no? Te permitirá iterar sobre el resultado sin ninguna condición adicional. Si es un número entero, tal vez sea suficiente con 0, si es un hash, un hash vacío. etc.
La premisa es no forzar el código de llamada para resolver problemas de inmediato. El código de llamada puede no querer preocuparse por ellos. Es por eso que en muchos casos las excepciones son mejores que cero.
Gday,
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 ni idea.
Editar: esto es cierto para los idiomas en los que no tiene excepciones, como C, donde ha sido la convención durante muchos años.
HTH
''Avahappy,
Haga que llamen a otro método después del hecho para averiguar si la llamada anterior fue nula. ;-) Oye, fue lo suficientemente bueno para JDBC
La idea base detrás de este hilo es programar a la defensiva. Es decir, codificar contra lo inesperado. Hay una variedad de respuestas diferentes:
Adamski sugiere mirar Patrón de objeto nulo, con esa respuesta siendo votado 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 de 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 "creadas" por un desarrollador: el autor
Clases "consumidas" por otro (tal vez) desarrollador: el desarrollador
Independientemente de si una clase devuelve NULL para los 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.
Por lo tanto, dado que NULL o el OBJETO NULO (por ejemplo, si (empleado como NullEmployee.ISVALID)) deben ser verificados y eso puede tener que 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 cuando ramifica su código de consumo.
Como desarrollador que consume la clase, espero que el autor me brinde la posibilidad de evitar o programar la situación nula a la que su clase / métodos pueden enfrentarse.
Entonces, como desarrollador, programaría defensivamente contra NULL desde un método. Si el autor me ha otorgado un contrato que siempre devuelve un objeto (NULL OBJECT siempre lo hace) y ese objeto tiene un método / propiedad para 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 los mecanismos que un desarrollador puede 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 de otra clase / método.
Saludos
GregJF
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 comprobar el patrón de objeto nulo que proporciona más información sobre esto.
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 es 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.
}
}
Las excepciones son para 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 pretende devolver una lista de atributos, y no hay ninguno para devolver, devolver una lista vacía tiene sentido; devuelve todos los atributos cero.
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 debería haber siempre un resultado no nulo, entonces podría ser 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 /* ... */ {
}
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 / error, devolver nulo en caso de error y un objeto para tener éxito no lo haría ser menos correcto, luego devolver verdadero / falso y obtener el objeto a través del parámetro.
Otro enfoque sería usar la excepción para indicar fallas, pero aquí - en realidad hay muchas más voces, que dicen que esta es una práctica MALA (ya que usar excepciones puede ser conveniente pero tiene muchas desventajas).
Así que personalmente no veo nada malo en devolver nulo como una indicación de que algo salió mal, y revisarlo 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 llevar a otros errores, a veces difíciles de encontrar (aunque en la mayoría de los casos, simplemente bloqueará su sistema :), ya que hará referencia a 0x00000000 tarde o temprano).
Para ciertos escenarios, quiere notar una falla tan pronto como suceda.
Verificar en contra de NULL y no afirmar (para errores del programador) o tirar (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 explotaciones de seguridad. Tal vez la nulidad proviene del hecho de que un buffer fue sobrescrito o similar. Ahora, no está fallando, lo que significa que el explotador tiene la oportunidad de ejecutar en su código.
Sí, devolver NULL es un diseño terrible , en un mundo orientado a objetos. En pocas palabras, el uso NULL conduce a:
- manejo de errores ad-hoc (en lugar de excepciones)
- semántica ambigua
- lento en lugar de fallar rápidamente
- Pensamiento informático en lugar de pensar en objetos
- objetos mutables e incompletos
Consulte esta publicación en el 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 , Section 4.1.
Si el código es algo así 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 lo devuelve en lugar de nulo en los casos apropiados, no tiene que verificar el caso nulo y, en cambio, puede hacerlo:
get_something_to_do().execute()
Por lo tanto, aquí el problema no es entre verificar NULL vs. una excepción, sino que se trata de que la persona que llama tenga que manejar no casos especiales de manera diferente (de la forma que sea) o no.
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:
- Si es posible devolver un valor neutral, hágalo .
Los enunciados vacíos, las cuerdas, etc. son buenos ejemplos - Si dicho valor neutral no existe, debe devolverse el valor nulo .
Como se mencionó, se supone que el método posiblemente no tenga ningún resultado, por lo que no es excepcional, por lo tanto, no debe arrojar una excepción. Un valor neutral no es posible (por ejemplo: 0 no es especialmente un resultado neutral, dependiendo del programa)
Hasta que tengamos una forma oficial de indicar que una función puede devolver nulo o no, intento 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 le pongo a mis métodos Safe Something () cuando el método arroja un resultado neutral en lugar de nulo.
Aún no estoy del todo de acuerdo con el nombre, pero no pude encontrar 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 lanza -
Get...
devuelve una instancia existente esperada, o arroja -
GetOrCreate...
devuelve una instancia existente, o una nueva instancia si no existe, o lanza -
Find...
devuelve una instancia existente, si existe, onull
Para consultas de cobranza:
-
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.