Como usar una interfaz con parametros polimorficos

publicado por: Anonymous

Tengo una duda sobre interfaces y su uso, primeramente les platico mi problema

  1. Tengo que construir un sistema en el cual tengo acciones del 1 al 10, cada acción tiene propiedades individuales, es decir son objetos diferentes

Aqui un ejemplo con dos dtos de acciones particulares (estos objetos son los que quiero pasar como parametros pero de manera genérica)

public class AccionUno{
    private BigInteger idAccionUno;
    private Date fechaDeElaboracion;
    private String observaciones;
    private String necesidadesDeInformacion;
    private String guiaAsignada;
    //getters y setters
}

public class AccionDos{
   private BigInteger idAccionDos;
   private List<String> hechos;    
   private String descripcion
   //getters y setters
}
  1. Cada acción tiene un comportamiento común, el cual es registrar, consultar, borrar, editar y validar, por lo cual quisiera tener una interfaz comun para todas las acciones
  2. Lo único que cambia es el tipo de parámetro que ingresa a la interfaz y en el caso de consultar el tipo de retorno también cambia, en la interfaz pongo un objeto genérico llamado Accion que quiero que sea la abstraccion de todas las acciones

Aqui un ejemplo de la interfaz y el problema radica en como abstraer un parametro de un tipo generico que represente a todas mis acciones

public interface Accionable{
    public void registrarAccion(Accion accion);
    public void borrarAccion(Accion accion);
    public void editarAccion(Accion accion);
    public Accion consultarAccion(Accion accion);
}

Y quisiera implementarlas de la siguiente manera

public class AccionUnoImplementacion implements Accionable{
    public void registrarAccion(Accion accion){
       //Registrar accion uno
    }
    public void borrarAccion(Accion accion){
       //Borrar accion uno
    }
    public void editarAccion(Accion accion){
       //Editar accion uno
    }
    public Accion consultarAccion(Accion accion){
       //Consultar accion uno
    }
}

public class AccionDosImplementacion implements Accionable{
    public void registrarAccion(Accion accion){
       //Registrar accion dos
    }
    public void borrarAccion(Accion accion){
       //Borrar accion dos
    }
    public void editarAccion(Accion accion){
       //Editar accion dos
    }
    public Accion consultarAccion(Accion accion){
       //Consultar accion dos
    }
}

He intentado realizarlo sobrescribiendo los a parametros especificos por ejemplo

public void registrarAccion(AccionUno accion);
public void registrarAccion(AccionDos accion);

Pero el problema con eso es que ya al hacerla común tengo que romper el principio de segregación de interfaces ya que cada cliente que ocupe dicha interfaz debe de implementar los n métodos aunque solo necesite uno

Tambien intente con genéricos como muestra este link

La justificación de la solución que busco es cumplir con el principio de open/close ya que si se pudiera lograr, no me importaría cuantas acciones se agreguen, todo lo específico a cada implementación estaría bien separado de la abstracción de lo que una acción puede hacer

Dicho lo anterior quisiera saber si existe alguna forma de:

  1. Realizar una solución como la que describo
  2. Como puedo lograr polimorfismo a través de los parámetros sabiendo que no son iguales

EDICIÓN Conforme a lo que mencionan en los comentarios, llegue a esta solución, por lo cual no se que piensen o si tenga algún tipo de falla o alguna mejora, ¿podría lograrse algo similar con interfaces?, algo así como el Duck typing de Ruby

Una Clase padre llamada Acción, NOTESE QUE NO SOLO SON GET Y SET, LAS CLASES CONTIENEN LOGICA DE ENCAPSULACION PARA CADA UNA

public class Accion {

private int idAccion;
private String observaciones;

    //getters y setters

/*
Inicio Logica de encapsulación de esta clase
 */
private boolean sonObservacionesNulas() {
    if (observaciones != null) {
        return sonObservacionesVacias();
    }
    return true;
}

private boolean sonObservacionesVacias() {
    return observaciones.trim().isEmpty();
}
/*
Fin Logica de encapsulacion de esta clase
 */

}

Una clase hija llamada AccionUno que extiende de Accion

public class AccionUno extends Accion {

private Set<String> necesidades;

//getters y setters

/*
Inicio Logica de encapsulacion de esta clase
 */
public boolean sonNecesidadesValidas() {
    return sonNecesidadesNulas();
}

private boolean sonNecesidadesNulas() {
    if (necesidades != null) {
        return sonNecesidadesVacias();
    }
    return true;
}

private boolean sonNecesidadesVacias() {
    return necesidades.isEmpty();
}
/*
Fin Logica de encapsulacion de esta clase
 */

}

Una clase hija llamada AccionDos que extiende de Accion

public class AccionDos extends Accion {

private Integer numeroDePersonasBeneficiadas;
private String hechos;

//getters y setters

/*
Inicio Logica de encapsulacion de esta clase
 */
public boolean sonHechosValidas() {
    return sonHechosNulos();
}

private boolean sonHechosNulos() {
    if (hechos != null) {
        return sonHechosVacias();
    }
    return true;
}

private boolean sonHechosVacias() {
    return hechos.trim().isEmpty();
}
/*
Fin Logica de encapsulacion de esta clase
 */

}

Una interfaz generica que recibe la clase padre Accion como parametro

public interface Accionable<T extends Accion> {

public void registrarAccion(T accion);

public Accion consultatAccion(T accion);

public void validarAccion(T accion);
}

Una clase implementadora correspondiente a la AccionDos

public class AccionDosImplementacion implements Accionable<AccionDos> {

@Override
public void registrarAccion(AccionDos accion) {
    System.out.println("#################################");
    System.out.println("Logica especifica para registrar una accion DOS en bd");
    accion.setIdAccion(1);
    accion.setNumeroDePersonasBeneficiadas(2);
    if (accion.getNumeroDePersonasBeneficiadas() >= 2) {
        System.out.println("Haz esto");
    } else {
        System.out.println("Esto otro");
    }
    accion.setHechos("hechos");
    accion.setObservaciones("observaciones");
    System.out.println("Registrar accion en tabla especifica de accion DOS");
}

@Override
public Accion consultatAccion(AccionDos accion) {
    System.out.println("Buscar en base de datos accionDos en particular");
    return accion;
}

@Override
public void validarAccion(AccionDos accion) {
    System.out.println("Buscar en base de datos accionDOS en particular para validar");
    System.out.println("Validar accion");
    System.out.println("#################################");
}

}

Una clase implementadora correspondiente a la AccionUno

public class AccionUnoImplementacion implements Accionable<AccionUno> {

@Override
public void registrarAccion(AccionUno accion) {
    System.out.println("#################################");
    System.out.println("Logica especifica para registrar una accion uno en bd");
    accion.setIdAccion(1);
    Set<String> necesidades = new HashSet<>();
    necesidades.add("necesidadUno");
    necesidades.add("necesidadDos");
    accion.setNecesidades(necesidades);
    accion.setObservaciones("observaciones");
    System.out.println("Registrar accion en tabla especifica de accion uno");
}

@Override
public Accion consultatAccion(AccionUno accion) {
    System.out.println("Buscar en base de datos accion en particular");
    return accion;
}

@Override
public void validarAccion(AccionUno accion) {
    System.out.println("Buscar en base de datos accion en particular para validar");
    System.out.println("Validar accion");
    System.out.println("#################################");
}

}

Cliente

public class BeanAccionN {

public static void main(String[] args) {
    //Empiezan comportamiento polimorfico con AccionUno
    Accionable accionable = new AccionUnoImplementacion();
    Accion accion = new AccionUno();
    registrar(accion, accionable);
    consultar(accion, accionable);
    validar(accion, accionable);

    //Empiezan comportamiento polimorfico con AccionDos
    accionable = new AccionDosImplementacion();
    accion = new AccionDos();
    registrar(accion, accionable);
    consultar(accion, accionable);
    validar(accion, accionable);

}

private static void registrar(Accion accion, Accionable accionable) {
    accionable.registrarAccion(accion);
}

private static void validar(Accion accion, Accionable accionable) {
    accionable.validarAccion(accion);
}

private static void consultar(Accion accion, Accionable accionable) {
    accionable.consultatAccion(accion);
}

}

solución

Lo que tienes que hacer es una clase padre de todas las acciones que es la que usará la interfaz Accionable. Esta clase padre debe tener el nombre Accion para mantener tu firma.

Todas las demás clases Accion* deben extenderla:

public class AccionUno extends Accion {
    //Código de esta clase
}
public class AccionDos extends Accion {
    //Código de esta otra clase
}

Así tus servicios implementarán la interfaz Accionable con la firma de método que has descrito para todas tus distintas clases que heredan de Accion:

public class AccionUnoService implements Accionable{

    public void registrarAccion(Accion accion){
        //Resto de código
    }
}

public class AccionDosService implements Accionable{

    public void registrarAccion(Accion accion){
        //Resto de código
    }
}
Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *