Por ejemplo, supongamos que tengo una clase Member
, que tiene un lastChangePasswordTime:
class Member{
.
.
.
constructor(){
this.lastChangePasswordTime=null,
}
}
cuyo lastChangePasswordTime puede ser significativo ausente, porque algunos miembros nunca pueden cambiar sus contraseñas.
Pero de acuerdo con Si los valores nulos son malos, ¿qué se debe usar cuando un valor puede estar significativamente ausente? y https://softwareengineering.stackexchange.com/a/12836/248528 , no debería usar nulo para representar un valor significativamente ausente. Así que trato de agregar una bandera booleana:
class Member{
.
.
.
constructor(){
this.isPasswordChanged=false,
this.lastChangePasswordTime=null,
}
}
Pero creo que es bastante obsoleto porque:
Cuando isPasswordChanged es falso, lastChangePasswordTime debe ser nulo, y comprobar lastChangePasswordTime == null es casi idéntico a comprobar isPasswordChanged es falso, por lo que prefiero comprobar lastChangePasswordTime == null directamente
Al cambiar la lógica aquí, puedo olvidar actualizar ambos campos.
Nota: cuando un usuario cambia las contraseñas, registraría el tiempo así:
this.lastChangePasswordTime=Date.now();
¿Es el campo booleano adicional mejor que una referencia nula aquí?
std::optional
oOption
. En otros idiomas, es posible que tenga que construir un mecanismo apropiado usted mismo, o en realidad puede recurrir anull
algo similar porque es más idiomático.lastChangePasswordTime
puede ser un puntero no inicializado allí, y compararlo con cualquier cosa sería un comportamiento indefinido. No es una razón realmente convincente para no inicializar el puntero aNULL
/ en sunullptr
lugar, especialmente no en C ++ moderno (donde no usarías un puntero), pero ¿quién sabe? Otro ejemplo sería idiomas sin punteros, o con mal soporte para punteros, tal vez. (FORTRAN 77 viene a la mente ...)Respuestas:
No veo por qué, si tiene un valor significativamente ausente,
null
no debe usarse si es deliberado y cuidadoso al respecto.Si su objetivo es rodear el valor anulable para evitar referenciarlo accidentalmente, sugeriría crear el
isPasswordChanged
valor como una función o propiedad que devuelva el resultado de una verificación nula, por ejemplo:En mi opinión, hacerlo de esta manera:
isPasswordChanged
valor que menciona.La forma en que usted conserva los datos (presumiblemente en una base de datos) sería responsable de garantizar que se conserven los valores nulos.
fuente
isPasswordChanged
al igual que puede olvidarse de verificar si es nulo. No veo nada ganado aquí.nulls
no son malvados Usarlos sin pensar es. Este es un caso dondenull
es exactamente la respuesta correcta: no hay fecha.Tenga en cuenta que su solución crea más problemas. ¿Cuál es el significado de la fecha establecida en algo, pero
isPasswordChanged
es falso? Acaba de crear un caso de información conflictiva que necesita capturar y tratar especialmente, mientras que unnull
valor tiene un significado claramente definido y no ambiguo y no puede entrar en conflicto con otra información.Entonces no, su solución no es mejor. Permitir un
null
valor es el enfoque correcto aquí. Las personas que afirman quenull
siempre es malo, sin importar el contexto, no entienden por quénull
existe.fuente
null
existe porque Tony Hoare cometió el error de mil millones de dólares en 1965. Las personas que afirman quenull
es "malvado" están simplificando demasiado el tema, por supuesto, pero es bueno que los lenguajes modernos o los estilos de programación se estén alejandonull
, reemplazando con tipos de opciones dedicados.DateTime
campo es un puntero. ¡Todo el concepto denull
solo tiene sentido para los punteros!Dependiendo de su lenguaje de programación, puede haber buenas alternativas, como un
optional
tipo de datos. En C ++ (17), esto sería std :: opcional . Puede estar ausente o tener un valor arbitrario del tipo de datos subyacente.fuente
Optional
en Java; el significado de unOptional
ser nulo depende de usted ...Optional<Date> lastPasswordChangeTime = null;
, pero no me rendiría solo por eso. En cambio, inculcaría una regla de equipo muy firme, que nunca se permite asignar valores opcionalesnull
. Opcional le compra demasiadas características agradables para renunciar a eso fácilmente. Puede asignar fácilmente valores predeterminados (orElse
o con una evaluación perezosa:)orElseGet
, arrojar errores (orElseThrow
), transformar valores de forma nula y segura (map
,flatmap
), etc.isPresent
es casi siempre un olor a código, en mi experiencia, y una señal bastante confiable de que los desarrolladores que usan estas opciones están "perdiendo el punto". De hecho, básicamente no ofrece ningún beneficioref == null
, pero tampoco es algo que deba usar con frecuencia. Incluso si el equipo es inestable, una herramienta de análisis estático puede establecer la ley, como un linter o FindBugs , que pueden detectar la mayoría de los casos.null
porque el lenguaje es 100% compatible con Java, pero nadie lo usa, yOption[T]
está en todas partes, con un excelente soporte de programación funcional comomap
,flatMap
coincidencia de patrones, etc. lejos, mucho más allá de los== null
controles. Tiene razón en que, en teoría, un tipo de opción suena como poco más que un puntero glorificado, pero la realidad demuestra que ese argumento es incorrecto.Utilizando correctamente nulo
Hay diferentes formas de usar
null
. La forma más común y semánticamente correcta es usarlo cuando puede o no tener un solo valor. En este caso, un valor es igualnull
o es algo significativo como un registro de la base de datos o algo así.En estas situaciones, generalmente lo usa así (en pseudocódigo):
Problema
Y tiene un gran problema. ¡El problema es que para cuando invocas
doSomethingUseful
el valor puede no haber sido verificadonull
! Si no fuera así, el programa probablemente se bloqueará. Y es posible que el usuario ni siquiera vea ningún mensaje de error bueno, quedando con algo como "error horrible: ¡valor deseado pero nulo!" (después de la actualización: aunque puede haber incluso menos errores informativos comoSegmentation fault. Core dumped.
, o peor aún, ningún error y manipulación incorrecta en nulo en algunos casos)Olvidar escribir cheques
null
y manejarnull
situaciones es un error extremadamente común . Es por eso que Tony Hoare, quien inventó,null
dijo en una conferencia de software llamada QCon London en 2009 que cometió el error de mil millones de dólares en 1965: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Error-Tony-HoareEvitando el problema
Algunas tecnologías e idiomas hacen que sea
null
imposible olvidar la comprobación de diferentes maneras, reduciendo la cantidad de errores.Por ejemplo, Haskell tiene la
Maybe
mónada en lugar de nulos. Supongamos queDatabaseRecord
es un tipo definido por el usuario. En Haskell, un valor de tipoMaybe DatabaseRecord
puede ser igualJust <somevalue>
o igual aNothing
. Luego puede usarlo de diferentes maneras, pero no importa cómo lo use, no puede aplicar alguna operaciónNothing
sin saberlo.Por ejemplo, esta función llamada
zeroAsDefault
devuelvex
paraJust x
y0
paraNothing
:Christian Hackl dice que C ++ 17 y Scala tienen sus propios modos. Por lo tanto, puede intentar averiguar si su idioma tiene algo así y usarlo.
Los nulos todavía se usan ampliamente
Si no tienes nada mejor, entonces usar
null
está bien. Solo mantente atento. Las declaraciones de tipo en funciones te ayudarán de alguna manera.Además, eso puede sonar no muy progresivo, pero debe verificar si sus colegas quieren usar
null
o algo más. Pueden ser conservadores y es posible que no quieran usar nuevas estructuras de datos por algunos motivos. Por ejemplo, admitir versiones anteriores de un idioma. Tales cosas deben declararse en los estándares de codificación del proyecto y debatirse adecuadamente con el equipo.En su propuesta
Sugiere usar un campo booleano separado. Pero debe verificarlo de todos modos y aún así puede olvidar verificarlo. Entonces no hay nada ganado aquí. Si incluso puede olvidar algo más, como actualizar ambos valores cada vez, entonces es aún peor. Si
null
no se resuelve el problema de olvidar verificar, entonces no tiene sentido. Evitarnull
es difícil y no debe hacerlo de tal manera que lo empeore.Cómo no usar nulo
Finalmente, hay formas comunes de usar
null
incorrectamente. Una de esas formas es usarlo en lugar de estructuras de datos vacías, como matrices y cadenas. ¡Una matriz vacía es una matriz adecuada como cualquier otra! Casi siempre es importante y útil para las estructuras de datos, que pueden ajustarse a múltiples valores, para poder estar vacías, es decir, tiene una longitud 0.Desde el punto de vista de álgebra, una cadena vacía para cadenas es muy similar a 0 para números, es decir, identidad:
La cadena vacía permite que las cadenas en general se conviertan en un monoide: https://en.wikipedia.org/wiki/Monoid Si no lo obtiene, no es tan importante para usted.
Ahora veamos por qué es importante para la programación con este ejemplo:
Si pasamos una matriz vacía aquí, el código funcionará bien. Simplemente no hará nada. Sin embargo, si pasamos un
null
aquí, es probable que tengamos un error con un error como "no se puede recorrer nulo, lo siento". Podríamos envolverlo,if
pero eso es menos limpio y, de nuevo, es posible que se olvide de verificarloCómo manejar nulo
Lo que
doSomethingAboutIt()
debería estar haciendo y especialmente si debería arrojar una excepción es otro tema complicado. En resumen, depende de sinull
fue un valor de entrada aceptable para una tarea determinada y de lo que se espera en respuesta. Las excepciones son para eventos que no se esperaban. No profundizaré más en ese tema. Esta respuesta ya es larga.fuente
horrible error: wanted value but got null!
es mucho mejor que el más típicoSegmentation fault. Core dumped.
...Error: the product you want to buy is out of stock
.null
dondenull
no está permitido es un error de programación, es decir, un error en el código. Un producto que está agotado es parte de la lógica comercial ordinaria y no un error. No puede ni debe intentar traducir la detección de un error a un mensaje normal en la interfaz de usuario. Bloquear de inmediato y no ejecutar más código es el curso de acción preferido, especialmente si es una aplicación importante (por ejemplo, una que realiza transacciones financieras reales).null
por completo, incluso desde el fondo.Además de todas las muy buenas respuestas dadas anteriormente, agregaría que cada vez que tenga la tentación de dejar un campo nulo, piense detenidamente si podría ser un tipo de lista. Un tipo anulable es equivalentemente una lista de 0 o 1 elementos, y a menudo esto puede generalizarse a una lista de N elementos. Específicamente en este caso, es posible que desee considerar
lastChangePasswordTime
ser una lista depasswordChangeTimes
.fuente
Pregúntese esto: ¿qué comportamiento requiere el campo lastChangePasswordTime?
Si necesita ese campo para un método IsPasswordExpired () para determinar si se debe solicitar a un Miembro que cambie su contraseña cada cierto tiempo, establecería el campo en el momento en que se creó inicialmente. La implementación IsPasswordExpired () es la misma para miembros nuevos y existentes.
Si tiene un requisito por separado de que los miembros recién creados tengan que actualizar su contraseña, agregaría un campo booleano separado llamado passwordShouldBeChanged y lo establecería en verdadero después de la creación. Luego, cambiaría la funcionalidad del método IsPasswordExpired () para incluir una verificación para ese campo (y cambiaría el nombre del método a ShouldChangePassword).
Haz explícitas tus intenciones en el código.
fuente
Primero, que los nulos sean malvados es un dogma y, como es habitual con el dogma, funciona mejor como guía y no como prueba de aprobación / no aprobación.
En segundo lugar, puede redefinir su situación de manera que tenga sentido que el valor nunca pueda ser nulo. InititialPasswordChanged es un valor booleano inicialmente establecido en falso, PasswordSetTime es la fecha y hora en que se configuró la contraseña actual.
Tenga en cuenta que si bien esto tiene un costo leve, ahora SIEMPRE puede calcular cuánto tiempo ha pasado desde la última vez que se configuró una contraseña.
fuente
Ambos son 'seguros / sanos / correctos' si la persona que llama verifica antes de usar. El problema es qué sucede si la persona que llama no marca. ¿Qué es mejor, algún sabor de error nulo o el uso de un valor no válido?
No hay una única respuesta correcta. Depende de lo que te preocupe.
Si los bloqueos son realmente malos pero la respuesta no es crítica o tiene un valor predeterminado aceptado, entonces quizás sea mejor usar un booleano como indicador. Si usar la respuesta incorrecta es un problema peor que estrellarse, entonces usar nulo es mejor.
Para la mayoría de los casos "típicos", la falla más rápida y obligar a las personas que llaman a verificar es la forma más rápida de obtener un código correcto y, por lo tanto, creo que nulo debería ser la opción predeterminada. Sin embargo, no confiaría demasiado en los evangelistas "X es la raíz de todo mal"; normalmente no han anticipado todos los casos de uso.
fuente