Estoy diseñando una interfaz con dos métodos relacionados, similares a este:
public interface ThingComputer {
default Thing computeFirstThing() {
return computeAllThings().get(0);
}
default List<Thing> computeAllThings() {
return ImmutableList.of(computeFirstThing());
}
}
Alrededor de la mitad de las implementaciones solo calcularán una cosa, mientras que la otra mitad puede calcular más.
¿Tiene esto algún precedente en el código Java 8 ampliamente utilizado? Sé que Haskell hace cosas similares en algunas clases de tipos ( Eq
por ejemplo).
Lo bueno es que tengo que escribir mucho menos código que si tuviera dos clases abstractas ( SingleThingComputer
y MultipleThingComputer
).
La desventaja es que una implementación vacía se compila pero explota en tiempo de ejecución con a StackOverflowError
. Es posible detectar la recursividad mutua con a ThreadLocal
y dar un error más agradable, pero eso agrega una sobrecarga al código sin errores.
java
design
interfaces
java8
Tavian Barnes
fuente
fuente
throw new Error();
o algo estúpido, solo que la interfaz en sí no debería tener un contrato frágil a través dedefault
métodos.abstract
existe para obligarlos a resolverlo.Respuestas:
TL; DR: no hagas esto.
Lo que muestra aquí es código frágil.
Una interfaz es un contrato. Dice "independientemente del objeto que obtenga, puede hacer X e Y". Como está escrito, su interfaz hace ni X ni Y porque está garantizado para causar un desbordamiento de pila.
Lanzar un error o una subclase indica un error grave que no debe detectarse:
Además, VirtualMachineError , la clase principal de StackOverflowError , dice esto:
Su programa no debe preocuparse por los recursos de JVM . Ese es el trabajo de la JVM. Hacer un programa que causa un error de JVM como parte de la operación normal es malo. Garantiza que su programa se bloqueará o obliga a los usuarios de esta interfaz a atrapar errores que no deberían preocuparle.
Es posible que conozca a Eric Lippert por sus esfuerzos como "miembro del comité de diseño del lenguaje C #" emérito. Habla sobre las características del lenguaje que empujan a las personas hacia el éxito o el fracaso: aunque esta no es una característica del lenguaje o parte del diseño del lenguaje, su punto es igualmente válido cuando se trata de implementar interfaces o usar objetos.
Fuente: C ++ y el pozo de la desesperación
Tener una interfaz que arroja
StackOverflowError
por defecto empuja a los desarrolladores al Pozo de la desesperación y es una mala idea . En cambio, empuje a los desarrolladores hacia el Foso del Éxito . Que sea fácil de usar interfaz de forma correcta y sin que se caiga la JVM.Delegar entre los métodos está bien aquí. Sin embargo, la dependencia debería ir en un sentido. Me gusta pensar en la delegación de métodos como un gráfico dirigido . Cada método es un nodo en el gráfico. Cada vez que un método llama a otro método, dibuje una ventaja desde el método de llamada al método llamado.
Si dibuja un gráfico y nota que es cíclico, es un olor a código. Ese es un potencial para empujar a los desarrolladores al Pozo de la Desesperación. Tenga en cuenta que no se debe prohibir categóricamente, solo que se debe tener precaución . Los algoritmos recursivos específicamente tendrán ciclos en el gráfico de llamadas: eso está bien. Documente y advierta a los desarrolladores. Si no es recursivo, intente romper ese ciclo. Si no puede, averigüe qué entradas pueden causar un desbordamiento de la pila y mitíguelas o documente como último caso si nada más funcionará.
fuente