Tengo una situación en la que intento recuperar un objeto. Si la búsqueda falla, tengo varios fallos en su lugar, cada uno de los cuales puede fallar. Entonces el código se ve así:
try {
return repository.getElement(x);
} catch (NotFoundException e) {
try {
return repository.getSimilarElement(x);
} catch (NotFoundException e1) {
try {
return repository.getParentElement(x);
} catch (NotFoundException e2) {
//can't recover
throw new IllegalArgumentException(e);
}
}
}
Esto se ve terriblemente feo. Odio volver nulo, pero ¿es eso mejor en esta situación?
Element e = return repository.getElement(x);
if (e == null) {
e = repository.getSimilarElement(x);
}
if (e == null) {
e = repository.getParentElement(x);
}
if (e == null) {
throw new IllegalArgumentException();
}
return e;
¿Hay otras alternativas?
¿Usar bloques anidados try-catch es un antipatrón? está relacionado, pero las respuestas allí están en la línea de "a veces, pero generalmente es evitable", sin decir cuándo o cómo evitarlo.
java
exception-handling
Alex Wittig
fuente
fuente
NotFoundException
algo realmente excepcional?Respuestas:
La forma habitual de eliminar el anidamiento es usar funciones:
Si estas reglas de respaldo son universales, podría considerar implementar esto directamente en el
repository
objeto, donde podría usar simplementeif
declaraciones simples en lugar de una excepción.fuente
method
sería una palabra mejor quefunction
.Esto sería realmente fácil con algo como una mónada Option. Desafortunadamente, Java no tiene esos. En Scala, usaría el
Try
tipo para encontrar la primera solución exitosa.En mi mentalidad de programación funcional, configuré una lista de devoluciones de llamada que representan las diversas fuentes posibles, y las recorrí hasta encontrar la primera exitosa:
Esto se puede recomendar solo si realmente tiene una gran cantidad de fuentes, o si tiene que configurar las fuentes en tiempo de ejecución. De lo contrario, esta es una abstracción innecesaria y se beneficiaría más de mantener su código simple y estúpido, y simplemente usar esos feos trucos anidados.
fuente
Try
tipo en Scala, por mencionar mónadas y por la solución usando un bucle.Optional
mónada ( prueba ) ya se había lanzado.Si está anticipando que se lanzarán muchas de esas llamadas al repositorio
NotFoundException
, puede usar un contenedor alrededor del repositorio para simplificar el código. No recomendaría esto para operaciones normales, ten en cuenta:fuente
A sugerencia de @ amon, aquí hay una respuesta que es más monádica. Es una versión muy resumida, donde debes aceptar algunas suposiciones:
la función "unidad" o "retorno" es el constructor de la clase
la operación de "vinculación" ocurre en tiempo de compilación, por lo que está oculta a la invocación
las funciones de "acción" también están vinculadas a la clase en tiempo de compilación
Aunque la clase es genérica y envuelve cualquier clase arbitraria E, creo que en realidad es exagerado en este caso. Pero lo dejé así como un ejemplo de lo que podrías hacer.
Con esas consideraciones, la mónada se traduce en una clase de envoltura fluida (aunque está renunciando a la flexibilidad que obtendría en un lenguaje puramente funcional):
(esto no se compilará ... ciertos detalles se dejan sin terminar para mantener la muestra pequeña)
Y la invocación se vería así:
Tenga en cuenta que tiene la flexibilidad para componer las operaciones de "búsqueda" como desee. Se detendrá cuando reciba una respuesta o una excepción que no sea encontrada.
Hice esto muy rápido; no está del todo bien, pero con suerte transmite la idea
fuente
repository.fetchElement().fetchParentElement().fetchSimilarElement();
- en mi opinión: código malvado (en el sentido dado por Jon Skeet)return this
para crear llamadas de objetos en cadena ha existido durante mucho tiempo. Como OO involucra objetos mutables,return this
es más o menos equivalente areturn null
sin encadenar. Sin embargo,return new Thing<E>
abre la puerta a otra capacidad en la que este ejemplo no entra, por lo que es importante para este patrón si elige seguir este camino.CustomerBuilder.withName("Steve").withID(403)
este código, y solo por verlo.fetchElement().fetchParentElement().fetchSimilarElement()
no está claro qué sucede, y esa es la clave aquí. ¿Todos van a buscarlos? No es acumulativo en este caso, y por lo tanto no es tan intuitivo. Necesito ver esoif (answer != null) return this
antes de que realmente lo entienda. Quizás es solo una cuestión de nombrar correctamente (orFetchParent
), pero de todos modos es "mágico".answer
engetAnswer
y de reposición (borrar) elanswer
campo mismo, antes de regresar su valor. De lo contrario, se rompe el principio de separación de comando / consulta, porque pedir recuperar un elemento (consulta) altera el estado de su objeto de depósito (answer
nunca se restablece) y afecta el comportamiento defetchElement
cuando lo llame la próxima vez. Sí, estoy un poco quisquilloso, creo que la respuesta es válida, no fui yo quien la rechazó.Otra forma de estructurar una serie de condiciones como esta es llevar una bandera, o probar nulo (mejor aún, use Guava's Opcional para determinar cuándo está presente una buena respuesta) para encadenar las condiciones.
De esa forma, observa el estado del elemento y realiza las llamadas correctas en función de su estado, es decir, siempre y cuando aún no tenga una respuesta.
(Sin embargo, estoy de acuerdo con @amon. Recomiendo mirar un patrón Monad, con un objeto de envoltura como
class Repository<E>
ese tiene miembrosE answer;
yException error;
. En cada etapa verifique si hay una excepción, y si es así, omita cada paso restante. Al final, te queda una respuesta, la ausencia de una respuesta o una excepción y puedes decidir qué hacer con eso).fuente
Primero, me parece que debería haber una función como
repository.getMostSimilar(x)
(debería elegir un nombre más apropiado) ya que parece haber una lógica que se utiliza para encontrar el elemento más cercano o más similar para un elemento dado.El repositorio puede implementar la lógica como se muestra en amons post. Eso significa que el único caso en que se debe lanzar una excepción es cuando no se puede encontrar un elemento único.
Sin embargo, esto solo es posible si las lógicas para encontrar el elemento más cercano se pueden encapsular en el repositorio. Si esto no es posible, proporcione más información sobre cómo (según qué criterios) se puede elegir el elemento más cercano.
fuente