2011-07-25 19 views
10

Muchos de mis funciones tiene una carga completa de código de validación justo debajo de las declaraciones:¿Existe alguna manera hermosa de afirmar las condiciones previas en los métodos de Java?

if (! (start < end)) { 
    throw new IllegalStateException("Start must be before end."); 
    } 

me gustaría especificar Precisa los rangos válidos de ciertos insumos - por ejemplo, un A> B, C => 1 o str_d.length()> 0.

Dado que algunas de mis funciones tienen bastantes argumentos que deben ser validados, puedo terminar escribiendo una gran cantidad de placa de caldera solo para validar las condiciones previas. Estoy escribiendo una biblioteca que será utilizada principalmente por desarrolladores no técnicos, hemos encontrado que validar las entradas de funciones es la mejor manera de ayudar a nuestros usuarios a operar correctamente nuestra API. Cuanto antes planteemos un error, menos trabajo tendrán que hacer nuestros clientes.

Hay un método más elegante para especificar las condiciones previas, las condiciones posteriores (y posiblemente las condiciones invariantes) en mis métodos.

Un colega me habló de una característica del lenguaje de programación de Eiffel que permite describir las condiciones pre/post/invariantes de forma muy natural sin repetir un gran número de códigos repetitivos. ¿Hay algún complemento en el lenguaje Java que me permita usar algo de esta magia?

Respuesta

5

¿Qué tal assert start < end. Eche un vistazo al documentation.

+1

Sí, soy principalmente un desarrollador de pthon. Esta es la sintaxis que usaríamos en Python, sin embargo, observo que este tipo de aserción generalmente se desactiva en el tiempo de ejecución. Si finalizara, la respuesta sería absurda y el usuario podría perder horas depurando el problema. Necesito levantar inmediatamente un error. –

+0

'assert start

-2

El paquete JUnit tiene construcciones como assert que ayudarían a hacer dicha comprobación de condición.

+0

Ya tengo este tipo de cosas en mis pruebas unitarias. –

+0

no solo tiene que usarlo en pruebas unitarias, también debería poder usarlo en tiempo de ejecución. – Milhous

+1

Junit libs no se debe utilizar en el código productivo – vvursT

5

Aspect oriented programming se puede utilizar para tal problema. Las llamadas a métodos se pueden interceptar comprobando la invariante. Pointcuts y consejos se configuran de manera declarativa. Spring y Guice hacen uso de AOP directo.

Aquí hay un example in Guice.

+1

Esto es interesante, ¿me puedes apuntar a un ejemplo en Guice? –

+1

No creo que Guice/AOP sea una buena sugerencia para la verificación previa general. Es ideal para grandes comprobaciones transversales (en particular, las que puede desear simplemente ingore en las pruebas) como "¿es la persona que hizo la solicitud un usuario conectado?", Pero verificaciones más específicas como "¿es A ColinD

4

Es posible que pueda hacer esto con anotación y programación orientada a aspectos.

Usaría IllegalArgumentException si una combinación de argumentos no es legal. Yo usaría IllegalStateException en un estado que impide que el método funcione.

Puede crear un método de ayuda para la excepción.

public static void check(boolean test, String message) { 
    if(!test) throw new IllegalArgumentException(message); 
} 

check(start < end, "Start must be before end."); 
+1

¡Definitivamente juegos de simplicidad! –

12

Guava 's Preconditions clase es sólo para esto. Normalmente, se utiliza con las importaciones estáticas, por lo que su ejemplo se vería así:

checkArgument(start < end, "Start must be before end"); 

Esto hace que sea fácil añadir más información para el mensaje, así, sin tener que pagar el costo de String concatenación si se aprueba la verificación.

checkArgument(start < end, "Start (%s) must be before end (%s)", start, end); 

A diferencia de las declaraciones assert, estas no se pueden deshabilitar.

6

Consulte el proyecto Cofoja que proporciona contratos para Java a través de anotaciones. Proporciona pre/postcondiciones e invariantes. También, a diferencia de otras implementaciones de Java, maneja correctamente los contratos definidos en las clases/interfaces principales. La evaluación del contrato se puede activar/desactivar en tiempo de ejecución.

Aquí es un fragmento de código de su tutorial:

import com.google.java.contract.Invariant; 
import com.google.java.contract.Requires; 

@Invariant("size() >= 0") 
interface Stack<T> { 
    public int size(); 

    @Requires("size() >= 1") 
    public T pop(); 

    public void push(T obj); 
} 
1

Para la validación de entrada, también se puede utilizar Apache Commons Validator.

Tenga en cuenta que la validación de entrada siempre debe estar habilitada. Por lo tanto, es conceptualmente muy diferente de la verificación de afirmación (como, por ejemplo, en Eiffel), que puede estar opcionalmente activada/desactivada. Consulte la respuesta a esta pregunta relacionada con el desbordamiento de la pila When should I use Apache Commons' Validate.isTrue, and when should I just use the 'assert' keyword?

1

Si me repito la misma caldera: código de comprobación de precondición de placa dentro de una clase, refactorizo ​​mi código para reducir la duplicación y para aumentar la abstracción extrayendo el código repetido en un nuevo método (static private). Uso el método Java-7 Objects.requireNonNull para las comprobaciones null.

+0

Esta debe ser la mejor opción si está construyendo un SDK que otros usarán. No se necesitan dependencias adicionales. :) – Carnell

Cuestiones relacionadas