2009-10-26 12 views
48

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. Teniendo en cuenta la información anterior, es el siguiente un diseño pobre (no acaba de sentirse derecha):Interfaz genérica

public interface IExecutesService<A,B> 
{ 
    public A executeService(); 
    public A executeService(B inputParameter); 
} 

Ahora, digamos que puedo implementar 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 la anterior:

  1. es el uso de un interace genérica (IExecutesService<A,B>) bien en el caso en que se desea proporcionar subclases, que requ ¿Hay diferentes parámetros de entrada y retorno para los métodos de interfaz?
  2. ¿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 tipo B en IExecutesService<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.

Agradeceré sus comentarios sobre esto.

Saludos.

+0

perfecta que estaba buscando esta – VedX

Respuesta

71

He aquí 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 tipo de borrado de cualquier clase sólo será capaz de poner en práctica uno de estos. Esto elimina el método redundante al menos.

No es una interfaz poco razonable que usted está proponiendo, pero tampoco estoy 100% seguro de qué valor agrega. Es posible que solo desee utilizar la interfaz estándar Callable. No admite argumentos, pero esa parte de la interfaz tiene el menor valor (imho).

+27

Gracias por no poner "I" delante de sus interfaces. –

+13

Prefijar interfaces con "I" es un .Net'ism real. Realmente no pertenece al código de Java. – cletus

+4

Antes de .NET, la notación húngara se usaba en el código desarrollado para Eclipse (y sus antecesores, VAME ...) en Zurich, por lo tanto, el uso de I para prefijar los nombres de interfaz en Eclipse hoy. –

2

Si entiendo correctamente, quiere 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 nada 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é cree 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?

+0

Solo por claridad, no es del todo cierto que las clases no mantengan CUALQUIER información. Todavía puede inspeccionar los límites de los parámetros (es decir, cómo se definió la clase/método genérico) a través de la reflexión. Sin embargo, los objetos no, por lo que no se puede decir cómo (con qué parámetros) se creó una instancia de un objeto. – falstro

+1

Hola. No quiero tener una clase implementando múltiples de estas interfaces. Una clase solo implementará una de estas interfaces: el problema es que una clase generalmente solo implementará uno de los métodos en la interfaz, dejando la otra redundante ... lo cual parece feo. La razón por la que elegí genéricos en la interfaz fue porque las subclases tendrán diferentes tipos de parámetros de devolución e ingreso dependiendo de las llamadas de servicio que implementen. ¿Esto aclara un poco las cosas? Gracias :) – user192585

+0

@roe, los límites (restricciones) en la clase están ahí, sí, pero eso no da ninguna información sobre el tipo efectivamente utilizado en una instancia específica (objeto). Mi sensación es que muchas personas que usan genéricos en Java no están conscientes de esto. – Lucero

18

Aquí hay otra sugerencia:

public interface Service<T> { 
    T execute(); 
} 

utilizando esta sencilla interfaz puede pasar argumentos a través del 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); 
    } 
} 
+0

Considerando la naturaleza remota de la interfaz, creo que tendría dos llamadas remotas. Esto podría ser costoso en cuanto a rendimiento :-( – KLE

+0

¿por qué debería tomar dos llamadas? Debe saber qué es lo que está llamando/creando (o la fábrica de servicio debería), de lo contrario, ¿cómo puede llamarlo? – Chii

8

me quedaría con dos interfaces diferentes.

Usted dijo que 'Quiero agrupar mis ejecutores de servicios bajo una interfaz común ... También parece excesivo 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 utilizarlo como marcador, puede aprovechar las anotaciones en su lugar.

Otro punto es que existe la posibilidad de que sus requisitos cambien y 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.

+1

Esto es la única respuesta correcta aquí. Sus dos grupos de servicios se acceden a través de métodos con diferentes firmas. No comparten una interfaz. Es así de simple. –

+0

@ user192585: su pregunta parece el resultado de una falta de imaginación para nombrar dos servicios diferentes Si continúa colocando dos grupos de servicios diferentes en la misma interfaz, puede terminar en una situación en la que tendrá una colección de objetos representados por esta interfaz única, pero para la cual se permitirá a algunos objetos un servicio mientras que los otros objetos se permite el otro pero esto, sin saber cuál es para cada objeto en la colección. Un buen ejemplo de confusión de código. – SylvainL

+0

también - qué sobre "I" en principios SÓLIDOS :) – mmmm

3

Como respuesta estrictamente conforme a su pregunta, apoyo la propuesta de cleytus.


También es posible usar una interfaz de marcador (con ningún método), dicen DistantCall, con varios varios sub-interfaces que tienen las firmas precisas que desee.

  • La interfaz general serviría para marcar todas ellas, en caso de que desee escribir algún código genérico para todas ellas.
  • El número de interfaces específicas se puede reducir mediante el uso de 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 a los comentarios del OP

no estaba pensando de cualquier instanceof en la convocatoria. Estaba pensando que tu código de llamada sabía lo que llamaba, y solo necesitabas ensamblar varias llamadas distantes en una interfaz común para algún código genérico (por ejemplo, auditando todas las llamadas distantes, por razones de rendimiento). En su pregunta, he visto ninguna mención de que el código de llamada es :-(genérica

Si es así, te sugiero que tienen una única interfaz, una sola firma. Tener varios que sólo traen una mayor complejidad, para nada.

Sin embargo, debe hacerse algunas preguntas más amplias:
cómo se asegurará de que la persona que llama y la persona que llama se comuniquen correctamente?

que podría ser un seguimiento de esta cuestión, o una pregunta diferente ...

+0

Puedo ver cómo funcionará esto, sin embargo, esto probablemente resulte en código que requiera verificar la instancia real que se está usando para llamar al método apropiado (por ejemplo xxx instanceof yyy) ... este tipo de programación es un código ¿oler? No parece una muy buena práctica de POO. ¿Alguien puede arrojar algo de luz sobre esto? Gracias – user192585

Cuestiones relacionadas