java - deobfuscator - decompiler procyon
Interfaz genérica (5)
Aquí hay otra sugerencia:
public interface Service<T> {
T execute();
}
usando esta interfaz simple puedes pasar argumentos a través de constructor en las clases de servicio concretas:
public class FooService implements Service<String> {
private final String input1;
private final int input2;
public FooService(String input1, int input2) {
this.input1 = input1;
this.input2 = input2;
}
@Override
public String execute() {
return String.format("''%s%d''", input1, input2);
}
}
Digamos que quería definir una interfaz que representa una llamada a un servicio remoto. Ahora, la llamada al servicio remoto generalmente arroja algo, pero también puede incluir parámetros de entrada. Supongamos que una clase implementadora generalmente solo implementará un método de servicio. Dada la información anterior, es el siguiente un diseño pobre (no se siente del todo bien):
public interface IExecutesService<A,B>
{
public A executeService();
public A executeService(B inputParameter);
}
Ahora, digamos que implemento esta interfaz con una clase que ejecuta un servicio remoto con un parámetro de entrada:
public class ServiceA implements IExecutesService<String,String>
{
public String executeService()
{
//This service call should not be executed by this class
throw new IllegalStateException("This method should not be called for this class...blabla");
}
public String executeService(String inputParameter)
{
//execute some service
}
Tengo dos preguntas con respecto a lo anterior:
- ¿El uso de una interfaz genérica (
IExecutesService<A,B>
) es bueno en el caso de que desee proporcionar subclases que requieren diferentes parámetros de entrada y tipos de retorno para los métodos de interfaz? - ¿Cómo puedo hacer lo anterior mejor? Es decir, quiero agrupar mis ejecutores de servicios bajo una interfaz común (
IExecutesService
); sin embargo, una clase implementadora típicamente solo implementará uno de los métodos, y el uso de una IllegalStateException se siente realmente desagradable. Además, el parámetro de tipo B enIExecutesService<A,B>
será redundante para una clase implementadora que llama a un servicio sin ningún parámetro de entrada. También parece excesivo crear dos interfaces separadas para las dos llamadas de servicio diferentes.
Aquí hay una sugerencia:
public interface Service<T,U> {
T executeService(U... args);
}
public class MyService implements Service<String, Integer> {
@Override
public String executeService(Integer... args) {
// do stuff
return null;
}
}
Debido al borrado de tipo, cualquier clase solo podrá implementar uno de estos. Esto elimina el método redundante al menos.
No es una interfaz irrazonable lo que está proponiendo, pero tampoco estoy 100% seguro de qué valor agrega. Es posible que solo desee utilizar la interfaz estándar de Callable
. No admite argumentos, pero esa parte de la interfaz tiene el menor valor (imho).
Como respuesta estrictamente en línea con su pregunta, apoyo la propuesta de cleytus.
También puede usar una interfaz de marcador (sin método), decir DistantCall
, con varias DistantCall
que tienen las firmas precisas que desea.
- La interfaz general serviría para marcarlos a todos, en caso de que quiera escribir algún código genérico para todos ellos.
- La cantidad de interfaces específicas se puede reducir utilizando la firma genérica de cleytus.
Ejemplos de interfaces ''reutilizables'':
public interface DistantCall {
}
public interface TUDistantCall<T,U> extends DistantCall {
T execute(U... us);
}
public interface UDistantCall<U> extends DistantCall {
void execute(U... us);
}
public interface TDistantCall<T> extends DistantCall {
T execute();
}
public interface TUVDistantCall<T, U, V> extends DistantCall {
T execute(U u, V... vs);
}
....
ACTUALIZADO en respuesta al comentario OP
No estaba pensando en ninguna instancia de la llamada. Estaba pensando que su código de llamada sabía lo que estaba llamando, y que solo tenía que ensamblar varias llamadas distantes en una interfaz común para algún código genérico ( por ejemplo, auditar todas las llamadas distantes, por razones de rendimiento ). En su pregunta, no he visto ninguna mención de que el código de llamada sea genérico :-(
Si es así, sugiero que solo tenga una interfaz, solo una firma. Tener varios solo traería más complejidad, por nada.
Sin embargo, debe hacerse algunas preguntas más amplias:
¿Cómo se asegurará de que la persona que llama y quien llama se comuniquen correctamente?
Eso podría ser un seguimiento de esta pregunta, o una pregunta diferente ...
Me quedaría con dos interfaces diferentes.
Usted dijo que ''quiero agrupar mis ejecutores de servicios bajo una interfaz común ... También parece demasiado crear dos interfaces separadas para las dos llamadas de servicio diferentes ... Una clase solo implementará una de estas interfaces''
No está claro cuál es el motivo para tener una sola interfaz en ese momento. Si desea usarlo como marcador, puede aprovechar las anotaciones en su lugar.
Otro punto es que existe un posible caso de que sus requisitos cambien y de que aparezcan métodos con otra firma en la interfaz. Por supuesto, es posible usar el patrón Adapter, pero sería bastante extraño ver que una clase en particular implementa una interfaz con, por ejemplo, tres métodos donde dos de ellos trow UnsupportedOperationException. Es posible que aparezca el cuarto método, etc.
Si entiendo correctamente, ¿desea que una clase implemente varias de esas interfaces con diferentes parámetros de entrada / salida? Esto no funcionará en Java, porque los genéricos se implementan mediante borrado.
El problema con los genéricos de Java es que los genéricos no son más que magia de compilación. En tiempo de ejecución, las clases no conservan ninguna información sobre los tipos utilizados para cosas genéricas (parámetros de tipo de clase, parámetros de tipo de método, parámetros de tipo de interfaz). Por lo tanto, aunque pueda tener sobrecargas de métodos específicos, no puede vincularlos a implementaciones de múltiples interfaces que difieren únicamente en sus parámetros de tipo genérico.
En general, puedo ver por qué piensas que este código tiene un olor. Sin embargo, para brindarle una mejor solución, sería necesario saber un poco más acerca de sus requisitos. ¿Por qué quieres usar una interfaz genérica en primer lugar?