una que polimorfismo metodos herencia elementos ejemplos crear como clases clase atributos atributo java oop inheritance instanceof

que - evitar instancia en Java



que es un atributo en java (9)

Aprendí sobre el Visitor pattern en la clase de compilación en la universidad, creo que podría aplicarse en su escenario Considere el siguiente código:

public class GameObjectVisitor { public boolean visit(GameObject1 obj1) { return true; } . . // one method for each game object public boolean visit(GameGroup obj1) { return true; } }

Y luego puedes poner un método en la interfaz de GameObject como este:

public interface GameObject { . . public boolean visit(GameObjectVisitor visitor); }

Y luego cada GameObject implementa este método:

public class GameGroup implements GameObject { . . . public boolean visit(GameObjectVisitor visitor) { visitor.visit(this); } }

Esto es especialmente útil cuando tienes una jerarquía de herencia compleja de GameObject . Para tu caso tu método se verá así:

private void allocateUITweenManager() { GameObjectVisitor gameGroupVisitor = new GameObjectVisitor() { public boolean visit(GameGroup obj1) { obj1.setUITweenManager(mUITweenManager); } }; for(GameObject go:mGameObjects){ go.visit(gameGroupVisitor); } }

En algún momento en la universidad me han dicho (y luego he leído en hasta catorce lugares) que usar instanceof solo debería usarse como ''último recurso''. Teniendo esto en cuenta, alguien puede decir si el siguiente código que tengo es un último recurso. He echado un vistazo alrededor del desbordamiento de pila pero no puedo encontrar un escenario similar, ¿quizás lo he perdido?

private void allocateUITweenManager() { for(GameObject go:mGameObjects){ if (go instanceof GameGroup) ((GameGroup) go).setUITweenManager(mUITweenManager); } }

dónde

  • mGameObjects es una matriz, solo algunos de los cuales son de tipo GameGroup
  • GameGroup es una subclase de la clase abstracta GameObject .
  • GameGroup usa la interfaz UITweenable que tiene el método setUITweenManager()
  • GameObject no usa la interfaz

Supongo que podría (y probablemente debería) reemplazar GameGroup en mi código anterior con UITweenable , me estaría preguntando lo mismo.

¿Hay alguna otra forma de hacer esto que evite la instanceof ? Este código no puede fallar, como tal (creo, ¿verdad?), Pero dada la mala impresión que tiene el instanceof , ¿he cometido algún pecado cardinal de OOP en algún lugar a lo largo de la línea que me hace usar instanceof ?

¡Gracias por adelantado!


El problema con este enfoque es que generalmente no aparece en un solo lugar en su código y, por lo tanto, hace que sea más o menos doloroso agregar otras implementaciones de la interfaz en el futuro. Si evitarlo depende de tu consideración. A veces se puede aplicar YAGNI y esta es la forma más directa.

Otros habían sugerido alternativas, por ejemplo, el patrón de Visitante.


El problema con instanceof es que puede sufrir futuros cambios en la jerarquía de objetos. El mejor enfoque es utilizar el Patrón de estrategia para los casos en los que es probable que use instanceof. Hacer una solución con ejemplos de lo que está cayendo en un problema La estrategia está tratando de resolver: a muchos ifs . Algunos chicos han fundado una comunidad. La campaña anti-IF podría ser una broma, pero untipattern es serio. En proyectos a largo plazo, mantener 10 a 20 niveles de if-else-if podría ser un dolor. En su caso, será mejor que setUITweenManager una interfaz común para todos los objetos de su matriz e implemente setUITweenManager para todos ellos a través de una interfaz.

interface TweenManagerAware{ setUITweenManager(UITweenManager manager); }


La razón por la que se desaconseja instanceof es porque en OOP no debemos examinar los tipos de objetos desde afuera. En cambio, la forma idiomática es dejar que los objetos actúen utilizando métodos anulados. En su caso, una posible solución podría ser definir boolean setUITweenManager(...) GameObject en GameObject y dejar que se vuelva true si es posible configurar el administrador para un objeto en particular. Sin embargo, si este patrón ocurre en muchos lugares, las clases de nivel superior pueden contaminarse bastante. Por lo tanto, a veces, instanceof es "mal menor".

El problema con este enfoque OPP es que cada objeto debe "conocer" todos sus posibles casos de uso. Si necesita una nueva función que funcione en su jerarquía de clases, debe agregarla a las clases en sí, no puede tenerla en algún lugar separado, como en un módulo diferente. Esto se puede resolver de forma general utilizando el patrón de visitante , como otros sugirieron. El patrón de visitante describe la forma más general de examinar objetos y se vuelve aún más útil cuando se combina con el polimorfismo.

Tenga en cuenta que otros idiomas (en particular lenguajes funcionales) utilizan un principio diferente. En lugar de dejar que los objetos "sepan" cómo realizan cada acción posible, declaran tipos de datos que no tienen métodos por sí mismos. En su lugar, el código que los utiliza examina cómo se construyeron utilizando la coincidencia de patrones en los tipos de datos algebraicos . Por lo que sé, el lenguaje más cercano a Java que tiene patrones de coincidencia es Scala . Hay un artículo interesante sobre cómo Scala implementa la comparación de patrones, que compara varios enfoques posibles: Hacer coincidir objetos con patrones. Burak Emir, Martin Odersky y John Williams.

Los datos en la programación orientada a objetos se organizan en una jerarquía de clases. El problema de la coincidencia de patrones orientada a objetos es cómo explorar esta jerarquía desde el exterior. Por lo general, esto implica clasificar los objetos por su tipo de tiempo de ejecución, acceder a sus miembros o determinar alguna otra característica de un grupo de objetos. En este artículo comparamos seis técnicas diferentes de coincidencia de patrones: descomposición orientada a objetos, visitantes, pruebas de tipo / tipografías, casos tipográficos, clases de casos y extractores. Las técnicas se comparan en nueve criterios relacionados con la concisión, la mantenibilidad y el rendimiento. El documento presenta las clases de casos y los extractores como dos nuevos métodos de coincidencia de patrones y muestra que su combinación funciona bien para todos los criterios establecidos.

En resumen: en OOP puede modificar fácilmente los tipos de datos (como agregar subclases), pero agregar nuevas funciones (métodos) requiere realizar cambios en muchas clases. Con ADT es fácil agregar nuevas funciones, pero modificar los tipos de datos requiere modificar muchas funciones.


Podría declarar setUITweenManager en GameObject con una implementación que no haga nada.

Podrías crear un método que devuelva un iterador para todas UITweenable instancias que se pueden consultar en la matriz de instancias GameObject .

Y hay otros enfoques que efectivamente ocultan el envío dentro de alguna abstracción; Por ejemplo, los patrones de visitante o adaptador.

... ¿He cometido algún pecado cardinal de OOP en algún lugar a lo largo de la línea que me tiene usando instanceof aquí?

En realidad no (IMO).

El peor problema con instanceof es cuando empiezas a usarlo para probar las clases de implementación. Y la razón que es particularmente mala es que hace que sea difícil agregar clases adicionales, etcétera. Aquí, la instanceof UITweenable cosas no parece presentar ese problema, porque la UITweenable parece ser más fundamental para el diseño.

Cuando realice este tipo de juicio, es mejor entender las razones por las cuales se afirma que la construcción o el uso (supuestamente) incorrecto es malo. Luego observa su caso de uso específico e inventa si estas razones se aplican, y si la alternativa que está viendo es realmente mejor en su caso de uso.


Puedes usar el contenedor mGameObjects para cuando necesites hacer algo en todos los objetos del juego y mantener un contenedor separado solo para los objetos de GameGroup.

Esto usará algo más de memoria, y cuando agregue / elimine objetos tendrá que actualizar ambos contenedores, pero no debería ser una sobrecarga notable, y le permite recorrer de manera muy eficiente todos los objetos.


Siempre es un poco "sospechoso" mezclar objetos de diferentes clases en la misma Colección. ¿Sería posible / tendría sentido dividir la única Colección de GameObjects en múltiples Colecciones, una de las meras GameObjects, otra de la UITweenables? (por ejemplo, use un MultiMap con clave por una Clase). Entonces podrías ir algo como:

for (UITweenable uit : myMap.get(UITweenable.class)) { uit.setUITweenManager(mUITweenManager); }

Ahora, todavía necesita una instanceof cuando se inserta en el mapa, pero está mejor encapsulado, oculto del código del cliente que no necesita conocer esos detalles

pd no soy un fanático de todas las "reglas" de SW, pero de Google "Principio de Sustitución de Liskov".


Tengo otra sugerencia de una manera de evitar el instanceof .

A menos que esté utilizando una fábrica de genéricos, en el momento en que cree un GameObject , sabrá qué tipo concreto es. Entonces, lo que puede hacer es pasar cualquier GameGroup s que cree un objeto observable, y les permite agregar oyentes a él. Funcionaría así:

public class Game { private void makeAGameGroup() { mGameObjects.add(new GameGroup(mUITweenManagerInformer)); } private void allocateUITweenManager() { mUITweenManagerInformer.fire(mUITweenManager); } private class OurUITweenManagerInformer extends UITweenManagerInformer { private ArrayList<UITweenManagerListener> listeners; public void addUITweenManagerListener(UITweenManagerListener l) { listeners.add(l); } public void fire(UITweenManager next) { for (UITweenManagerListener l : listeners) l.changed(next); } } private OurUITweenManagerInformer mUITweenManagerInformer = new OurUITweenManagerInformer(); } public interface UITweenManagerInformer { public void addUITweenManagerListener(UITweenManagerListener l); } public interface UITweenManagerListener { public void changed(UITweenManager next); }

Lo que me lleva a esta solución es:

  • Debido a que un UITweenManagerInformer es un parámetro constructor para GameGoup , no puedes olvidar pasarlo uno, mientras que con un método de instancia puedes olvidarte de llamarlo.

  • Para mí, tiene un sentido intuitivo que la información que necesita un objeto (como la forma en que un GameGroup necesita conocer el actual UITweenManager ) se debe pasar como parámetro de constructor. Si no tiene conocimiento del actual UITweenManager , no debe crear un GameGroup , y esta solución lo aplica.

  • instanceof nunca se utiliza.


EDITAR

Hay dos cosas principales que puede hacer aquí para liberarse de esta instancia específica de instanceof . (¿retruécano?)

  1. Haga lo que mi respuesta inicial sugirió y mueva el método que está apuntando hacia la clase que está iterando. Esto no es ideal en este caso, porque el método no tiene sentido para el objeto principal, y sería contaminante como lo ha puesto Ted.

  2. Reduzca el alcance de los objetos que está iterando solo a los objetos que están familiarizados con el método de destino. Creo que este es el enfoque más ideal, pero puede que no sea viable en la forma actual de su código.

Personalmente, evito el instanceof la plaga, porque me hace sentir que me he perdido algo, pero hay momentos en que es necesario. Si su código se presenta de esta manera, y no tiene forma de reducir el alcance de los objetos que está iterando, es probable que el instanceof funcione correctamente. Pero esta parece una buena oportunidad para ver cómo el polimorfismo puede hacer que su código sea más fácil de leer y mantener en el futuro.

Estoy dejando la respuesta original a continuación para mantener la integridad de los comentarios.

/EDITAR

Personalmente, no creo que esta sea una buena razón para usar instanceof . Me parece que podrías utilizar un poco de polimorfismo para lograr tu objetivo.

¿Has considerado hacer que setUITweenManager(...) un método de GameObject ? ¿Tiene sentido hacer esto?

Si tiene sentido, podría hacer que su implementación predeterminada no haga nada, y hacer que su GameGroup anule el método para hacer lo que usted quiere que haga. En este punto, su código podría verse así:

private void allocateUITweenManager() { for(GameObject go:mGameObjects){ go.setUITweenManager(mUITweenManager); } }

Esto es un polimorfismo en acción, pero no estoy seguro de que sea el mejor enfoque para su situación actual. Tendría más sentido iterar la Collection de objetos de UITweenable de la UITweenable en su lugar, si es posible.