Si tiene algún caso en el que un resultado o entrada no puede ser manejado por su programa, entonces eso debería ser un error. Debe saber lo que su programa puede manejar y permitir solo eso. En lo que respecta a posibles casos futuros en los que podría manejarse un resultado, pero todavía no, sugeriría considerarlo un error hasta que realmente necesite ese resultado. Si tiene dudas, no sabe, el programa no sabe, no se puede manejar, no ha equipado su programa para manejarlo, así que es un error. El programa no puede hacer nada más que detenerse.
Para la entrada del usuario es una muy mala idea confiar en su programa para eventualmente fallar. No sabe cuándo o incluso si se bloqueará. Podría terminar haciendo lo incorrecto o hacer diez cosas y luego colgarse que no debería haberlo hecho y no lo habría hecho si la entrada hubiera sido validada.
En términos de protección contra sus propios errores que es más de una bolsa mixta. Querrá enfocarse mucho más en asegurarse de que las cosas funcionen probando, eliminando incógnitas, revisando, asegurándose de saber exactamente cómo funciona su programa, etc. De todos modos, ocasionalmente habrá casos en que el procesamiento interno pueda producir resultados no deseados.
Cuando resulta que un resultado no es un error en un caso determinado, no maneja la excepción, maneja nulo, no una excepción. En ese caso, el resultado no es un error. Entonces manejas el resultado y no el error. No podría ser más simple. Si vas a tomar una excepción y luego hacer algo que se puede hacer con un if, tales como:
try
extra = i_need_it(extra_id)
show('extra', extra)
catch
show('add_extra')
Entonces eso no es correcto. Tienes un curso de acción perfectamente aceptable si no tienes algo extra.
Esto es mucho mejor y que mantiene su intención clara y sin el nivel de detalle adicional:
Something extra = i_want_it(extra_id)
if extra ==== null
show('add_extra')
else
show('extra', extra)
Aviso aquí se necesita nada especial para evitar la captura de una excepción de otra capa. Cómo puse try catch arriba es una mala práctica.Sólo se debe envolver la cosa que produce una excepción:
Something extra
try
extra = i_need_it(extra_id)
if extra === null
show('add_extra')
else
show('extra', extra)
Cuando cosa sobre ella de esa manera, entonces se acaba convirtiendo a excepción nula y luego de vuelta otra vez. Esta es la codificación YoYo.
usted debe comenzar con:
Object i_need_it(int id) throws
Hasta que no esté en condiciones de aplicar el manejo de nulo. Si puede implementar el manejo de la excepción, puede implementar el manejo del nulo.
Cuando resulta que algo que no siempre es necesaria, ya sea añadir o cambiar este método i_need_it a ella (si es nula siempre se maneja):
Object|null i_want_it(int id)
Una alternativa es comprobar es que existe en primer lugar:
bool does_it_exist(int id)
La razón esto no se hace tan a menudo se debe a que por lo general viene a cabo de esta manera:
if(does_it_exist(id))
Something i_need = i_need_it(id)
Esto tiende a ser más propenso a problemas de concurrencia, puede requerir más llamadas que podrían no ser confiables (IO en la red) y puede ser ineficiente (dos RTT en lugar de uno). Otras llamadas a menudo se fusionan de esta manera, como actualización si existe, insertar si es único, actualizar si existe o insertar, etc. que luego devuelve lo que normalmente sería el resultado de la verificación inicial. Algunos de estos tienen conflictos sobre la eficiencia del tamaño de la carga útil y la eficiencia de RTT, que también pueden variar en función de una serie de factores.
Sin embargo, es más barato cuando necesita un comportamiento alternativo basado en si algo existe o no, pero no necesita trabajar en él. Si no necesita preocuparse por las preocupaciones anteriores, es un poco más claro.
puede que incluso quiere:
void it_must_exist(int id) throws
puede resultar muy útil porque si sólo necesita asegurarse de algo existe a menudo es más económico que obtenerla. Sin embargo, es raro que lo necesite, ya que en la mayoría de los casos querrá saber si algo existe para trabajar directamente en él.
Una forma de concebir es que no haría 'does_it_exist' lanzar una excepción cuando simplemente puede devolver un booleano explícitamente en la pila de llamadas, 'i_want_it' es un 'has' y 'get' combinados en efecto .
Si bien tener dos métodos separados separa más claramente firmas de métodos, a veces es posible que tenga que pasar por debajo de otra cosa y la forma más sencilla para que si no la mía un poco de ambigüedad es:
Object|null get(int id, bool require) throws
Esto es mejor ya que está entregando el contrato por la cadena de llamadas en lugar de construir en una casa de arena basada en la acción a distancia. Hay formas de pasar su contrato de forma más explícita, pero tiende a ser YAGNI intrincado (es decir, transmitir una llamada del método).
Debe lanzar las excepciones con anticipación y puede querer estar seguro en lugar de lamentar, por lo que los falsos positivos están bien. Una vez que descubras que es un falso positivo, lo arreglarás en la fuente.
Debe ser extremadamente raro que esté manejando excepciones en absoluto.La gran mayoría debería llegar a la cima, luego invocar un controlador de salida y registro. El resto se soluciona de manera adecuada devolviendo el resultado directamente y administrándolo. Cuando tiene un falso positivo de muchos usos, solo este que lo usa. No solo elimine el cheque en la raíz y rompa los muchos otros casos donde todavía hay un error.
Java es un lenguaje desafortunado porque no se puede decir que no pase nulo o que esta variable no sea nula.
Cuando falta esta característica, a menudo es mejor buscar nulos en sus fuentes, cosas como IO en vez de cada vez que se pasa algo a uno de sus métodos. De lo contrario, es una cantidad absurda de comprobación nula.
Puede aplicar este patrón para crear funciones para reemplazar su ifs para la verificación de parámetros si realmente lo necesita. Se podría sustituir id con el objeto en sí, tales como:
Object i_want(Object it) throws
if(it === null)
throw
return it;
continuación:
void give_me(Object it)
this.it = Lib<Object>::i_want(it)
Una pena no hay ningún tipo de tránsito.
O:
void i_get_it(Getter<Object> g)
this.it = Lib<Object>::i_want(g.gimme())
Es posible que tenga un captador específico en lugar de con el genérico.
O:
void i_need_it(Result<Object> r)
this.it = r.require()
Si sólo lo hace en el límite vez (cuando se llama a las cosas fuera del lado de su control, donde no nulo resultado no puede garantizarse), aunque se prefiere hacer allí o para cualquier uso de un método documentado como devolver nulo y solo allí, ya que es donde realmente solo se necesita, eso significa que cuando obtienes un nulo al que no pertenece (los errores suceden) no vas a tener un tiempo fácil para descubrir de dónde vino. Se puede pasar mucho de IO y no producir una excepción de puntero nulo hasta que algo intente operar en él. Tales nulos se pueden pasar por el sistema durante días o incluso meses como una bomba de tiempo que está esperando estallar.
No lo haría yo mismo pero no culparía a la gente en algunos casos por implementar el enfoque de programación defensiva anterior que podría ser necesario debido al sistema de tipos rotos de Java que está suelto por defecto y no puede restringirse. En lenguajes robustos, null no está permitido a menos que explícitamente lo digas.
Tenga en cuenta que aunque lo llamo roto, he usado lenguajes significativamente más flexibles durante décadas para construir sistemas grandes, complejos y críticos sin tener que ensuciar la base de código con controles superfluos. La disciplina y la competencia son las que determinan la calidad.
Un falso positivo es cuando se produce un resultado o una condición que supone que es un resultado que no puede ser manejado por todas las personas que llaman, pero resulta que al menos una persona que llama puede manejarlo adecuadamente. En ese caso, no maneja la excepción sino que le da el resultado al que llama. Hay muy pocas excepciones a esto.
Java 8 tiene Opcional pero realmente no parece útil. Es un caso espantoso del efecto de plataforma interna que intenta implementar una nueva sintaxis como clases y termina teniendo que agregar la mitad de la sintaxis de Java existente, lo que la hace muy torpe. Como suele ser habitual en Java OOP, resuelve todos los problemas de las personas que quieren menos chocolate agregando más dulce de azúcar y complicando las cosas.Si realmente quieres encadenar de esa manera, deberías probar algo como kotlin, que implementa esa sintaxis directamente.
un lenguaje moderno, significa que usted no tiene que preocuparse por la mayor parte de esto y simplemente tener:
void i_need_it(Object it)
this.it = it
void i_want_it(Object? it)
this.it = it
Un lenguaje moderno incluso podría devolver el objeto original para un método sin un retorno (sustituir vacío con auto como el estándar y el auto-retorno) para que la gente pueda tener su encadenamiento elegante o cualquier otra cosa que esté de moda estos días en la programación.
No puede tener una fábrica con una clase base que le proporcione un Null o NotNull, ya que igual tendrá que pasar la clase base y eso será una violación de tipo cuando diga que quiere NotNull.
Es posible que desee jugar con los aspectos, análisis estático, etc. aunque eso es un poco un agujero de conejo. Toda la documentación debe indicar si puede devolverse el valor nulo (aunque indirectamente, se puede omitir).
Puedes hacer una clase como MightHave para envolver tu resultado en donde puedes poner en práctica métodos como get, require y has si no te gustan las estáticas pero quieres comer y si bien también está en el ámbito de la leve convolución y jugando con todas sus firmas de métodos en todas partes, boxeando todo por todos lados, una solución fea. Es realmente útil como una alternativa a esos raros casos de manejo de excepciones donde las excepciones parecen útiles debido a la complejidad del gráfico de llamadas y el número de incógnitas presentes en las capas.
Un caso excepcionalmente raro es cuando su fuente sabe qué excepción arrojar, pero no si debe arrojarse, pero es difícil de transmitir (aunque el acoplamiento de dos capas a una distancia donde algo puede suceder entre ellas debe abordarse con precaución). Algunas personas también pueden querer esto porque puede dar fácilmente una pista de dónde vino el elemento faltante y dónde se lo requirió, lo cual es algo que los controles pueden no dar (es probable que fallen cerca de la fuente, pero no se garantiza). Se debe tener precaución ya que este tipo de problemas podrían ser más indicativos de un control de flujo deficiente o una complejidad excesiva que un problema con polimorfismo justificado, codificación meta/dinámica y similares.
Se debe tener cuidado con cosas tales como valores por omisión o el patrón de objeto nulo. Ambos pueden terminar ocultando errores y convirtiéndose en las mejores suposiciones o una cura peor que la enfermedad. Cosas tales como un patrón NullObject y Opcional a menudo se pueden utilizar para simplemente desactivar o más bien bi-pasar el manejo de errores incorporado en su intérprete.
predeterminados no son siempre malas, pero pueden caer en el ámbito de adivinar. Ocultar los errores del usuario termina configurándolos para que fallen. Los valores predeterminados (y la desinfección) siempre deben pensarse cuidadosamente.
Cosas como el patrón NullObject y Opcional pueden ser utilizadas en exceso para desactivar eficazmente la comprobación del puntero nulo. Simplemente hace la suposición de que nulo nunca es un error que termina con programas haciendo algo, no otros, pero usted no sabe qué. En algunos casos, esto puede tener resultados divertidísimos, como si la tarjeta de crédito del usuario es nula. Asegúrate de utilizar estas cosas si no las estás usando para simplemente envolver todo tu código en una captura de prueba que ignore una excepción de puntero nulo. Esto es muy común porque las personas tienden a corregir errores donde se lanzó la excepción. En realidad, el error real tiende a estar más alejado. Usted termina con una falla en el manejo de IO que devuelve erróneamente nulo y ese nulo pasa por todo el programa. En lugar de solucionar esa única fuente de nulo, la gente intentará solucionarlo en todos los lugares donde alcanza un error.
patrón o patrones NullObject MagicNoop tienen su lugar, pero no son de uso común.No debe usarlos hasta que sea inmediatamente evidente que son útiles de una manera justificada que no va a causar más problemas de los que resuelve. Algunas veces, un noop es efectivamente un error.
ack ... no atrapar Excepción ... ¿y si el divisor es CERO? Luego también tomará la ArithmeticException de la división por cero, pero la tratará como la excepción de puntero Null "esperada". Sea tan específico acerca de las excepciones que desea atrapar como sea posible. – TofuBeer
¿Alguien quiere sacar el cuello y decir que una declaración if es menos costosa que una excepción lanzada? – AJay
Sí, lo hago. Una declaración 'if' es * mucho * menos costosa que una excepción lanzada. – EJP