Pregunta

No quiero discutir los méritos de este enfoque, sólo si es posible.Creo que la respuesta sea "no".Pero tal vez alguien me sorprenderá!

Imagine que usted tiene un núcleo de la clase widget.Tiene un método calculateHeight(), que devuelve una altura.La altura es demasiado grande - este resultado en los botones de (decir) que son demasiado grandes.Puede ampliar DefaultWidget para crear su propio NiceWidget, y la implementación de sus propios calculateHeight() para devolver un mejor tamaño.

Ahora una biblioteca de clase WindowDisplayFactory, se crea una instancia de DefaultWidget de una manera bastante compleja método.Usted desea que se utilice su NiceWidget.La clase de fábrica del método se ve algo como esto:

public IWidget createView(Component parent) {
    DefaultWidget widget = new DefaultWidget(CONSTS.BLUE, CONSTS.SIZE_STUPIDLY);

    // bunch of ifs ...
    SomeOtherWidget bla = new SomeOtherWidget(widget);
    SomeResultWidget result = new SomeResultWidget(parent);
    SomeListener listener = new SomeListener(parent, widget, flags);

    // more widget creation and voodoo here

    return result;
}

Ese es el trato.El resultado tiene el DefaultWidget profundo dentro de una jerarquía de otros objetos.La pregunta - cómo conseguir que este método de fábrica para usar mi propio NiceWidget?O, al menos, tener mi propia calculateHeight() en allí.Idealmente, me gustaría ser capaz de mono parche de la DefaultWidget para que su calculateHeight hizo lo correcto...

public class MyWindowDisplayFactory {
    public IWidget createView(Component parent) {
        DefaultWidget.class.setMethod("calculateHeight", myCalculateHeight);
        return super.createView(parent);
    }
}

Que es lo que yo podría hacer en Python, Ruby, etc.Me he inventado el nombre setMethod() sin embargo.El resto de las opciones abiertas para mí son:

  • Copiar y pegar el código de la createView() método en mi propia clase que hereda de la clase de fábrica
  • Vivir con los widgets que son demasiado grandes

La clase de fábrica no se puede cambiar - es parte de una plataforma clave de API.Traté de reflexión sobre el resultado devuelto a obtener el widget que (eventualmente) se añadió, pero se trata de varios widget-las capas de abajo y en algún lugar se lo usa para inicializar otras cosas, causando extraño efectos secundarios.

Alguna idea?Mi solución hasta ahora es la de copiar-pegar de trabajo, pero eso es un policía que requiere el seguimiento de los cambios en la fábrica principal de la clase al actualizar a nuevas versiones de la plataforma, y me interesaría escuchar otras opciones.

¿Fue útil?

Solución

Tal vez usted podría utilizar la Programación Orientada a Aspectos para interceptar las llamadas a la función y volver a su propia versión en su lugar?

La primavera ofrece algunas AOP funcionalidad, pero hay otras bibliotecas que hacerlo así.

Otros consejos

Uno feo solución sería poner su propia implementación de DefaultWidget (con el mismo FQCN) anterior en el Classpath de la aplicación normal.Es un terrible hachazo, pero cada otro enfoque en el que puedo pensar es aún peor.

Sólo mi idea del concepto,

Es posible que el uso de AOP, con código de bytes de ingeniería manera, para inyectar un aspecto a la calculateHeight método.

A continuación, puede permitir la revisión por ThreadLocal o más variables.

cglib es una biblioteca de Java que se pueden hacer algunas cosas similares a monkey patching - se puede manipular el código de bytes en tiempo de ejecución para cambiar ciertos comportamientos.No estoy seguro de si se puede hacer exactamente lo que usted necesita, pero vale la pena un vistazo...

Es totalmente posible monkeypatch en Java, utilizando Inseguro.putObject y una clase de buscador.Escribió un post en el blog aquí:

https://tersesystems.com/blog/2014/03/02/monkeypatching-java-classes/

El orientado a objetos manera de hacerlo sería crear un contenedor de la aplicación de IWidget, delegando todas las llamadas para el widget real, excepto calculateHeight, algo como:

class MyWidget implements IWidget {
    private IWidget delegate;
    public MyWidget(IWidget d) {
        this.delegate = d;
    }
    public int calculateHeight() {
        // my implementation of calculate height
    }
    // for all other methods: {
    public Object foo(Object bar) {
        return delegate.foo(bar);
    }
}

Para que esto funcione, usted necesita para interceptar todas las creaciones de el widget que desea reemplazar, lo que probablemente significa la creación de una similar contenedor para el Fabricawidget.Y usted debe ser capaz de configurar que Fabricawidget a utilizar.

También depende de ningún cliente tratando de lanzar la IWidget de vuelta a DefaultWidget...

Sólo sugerencias que puedo pensar:

  1. Cavar a través de la API de biblioteca para ver si hay alguna manera de anular los valores predeterminados y el tamaño.Tamaño puede ser confuso en el swing (al menos para mí) , setMinimum, setMaximum, setdefault, setDefaultOnThursday, ....Es posible que haya un camino.Si usted puede contactar con la biblioteca de diseñador(s), usted podría encontrar una respuesta que va a aliviar la necesidad de desagradable hacking.

  2. Tal vez extender la fábrica sólo reemplazar algunos predeterminado de tamaño de parámetro?depende de la fábrica, pero podría ser posible.

Crear una clase con el mismo nombre, puede ser la única otra opción, como otros han señalado que es feo, y lo más probable es que se olvide de él y romper cosas cuando la actualización de la api de la biblioteca o de implementar en un entorno diferente y olvidar por qué tenía el classpath estableció de esa manera.

Usted puede intentar el uso de herramientas como PowerMock/Mockito.Si usted puede burlarse de pruebas, usted puede burlarse de la producción también.

Sin embargo, estas herramientas no son realmente diseñado para ser utilizado de esa manera, así que tendrás que preparar el entorno de ti mismo y no ser capaz de utilizar JUnit corredores como en las pruebas de...

Bueno, sigo tratando de exponer sugerencias, y luego veo que no van a trabajar o que ya he mencionado que los probé.

La mejor solución que se me ocurre es subclase WindowDisplayFactory, a continuación, en la subclase de la createView() método, primero debe llamar a super.createView(), a continuación, modificar el objeto devuelto por completo tirar el widget y reemplazarlo con una instancia de la subclase que hace lo que quiere.Pero el widget se utiliza para inicializar las cosas, de manera que tendría que ir a cambiar todo.

Entonces pienso en el uso de la reflexión sobre el objeto devuelto de createView() y tratando de arreglar las cosas de esa manera, pero de nuevo, que peludo porque tantas cosas se inicializa con el widget.Creo que me gustaría probar a usar ese enfoque, aunque, si era lo suficientemente simple como para que se justifique más que copiar y pegar.

Voy a estar viendo esto, y pensando a ver si me puede venir con otras ideas.La Reflexión Java seguro que es bonito, pero no puede vencer a la dinámica de la introspección he visto disponible en lenguajes como Perl y Python.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top