una que privada otra objeto nombre llevan internas dentro crear clases clase anonimas abstractas java jboss class-design

java - que - crear un objeto de una clase dentro de otra clase



Grandes clases internas y variables privadas (5)

No soy fanático del uso excesivo de las clases internas. Creo que en realidad no ofrecen ninguna ventaja (cuando se usan en extremo) que poner el código en una clase normal no lo haría, y solo sirven para que el archivo de la clase sea innecesariamente grande y difícil de seguir.

¿Cuál es el daño si tiene que aumentar la visibilidad de algunos métodos? ¿Va a romper completamente tu abstracción o interfaz? Creo que con demasiada frecuencia los programadores tienden a hacer que todo sea privado por defecto, donde no hay mucho daño al permitir que otras clases llamen a tus métodos, si tu diseño está realmente basado en OO, eso es.

Si todas sus "clases internas de ayuda" necesitan acceso a algunos de los mismos métodos, considere ponerlos en una clase base para que puedan ser compartidos a través de la herencia.

Una cosa con la que me he encontrado varias veces es una clase de servicio (como un servicio de JBoss) que se ha vuelto demasiado grande debido a las clases internas de ayuda. Todavía tengo que encontrar una buena manera de romper la clase. Estos ayudantes generalmente son hilos. Aquí hay un ejemplo:

/** Asset service keeps track of the metadata about assets that live on other * systems. Complications include the fact the assets have a lifecycle and their * physical representation lives on other systems that have to be polled to find * out if the Asset is still there. */ public class AssetService { //...various private variables //...various methods public AssetService() { Job pollerJob = jobService.schedule( new AssetPoller() ); Job lifeCycleJob = jobService.schedule( AssetLifecycleMonitor() ); } class AssetPoller { public void run() { // contact remote systems and update this service''s private variables that // track the assets. } } class AssetLifecycleMonitor { public void run() { // look for assets that have meet criteria for a lifecycle shift // and update this service''s private variables as relevant. } } }

Entonces, ¿qué puede pasar si tengo un par de ayudantes y son en absoluto complejos, es que el archivo de clase general puede ser realmente grande? Me gustan las clases internas porque deja en claro que las clases son propiedad exclusiva del servicio y existen solo para ayudar a ese servicio. Intenté dividir las clases y pasar el servicio para padres como referencia, que funciona principalmente, pero las cosas que no me gustan son:

  • Termino exponiendo accesos a nivel de paquete para que las clases desglosadas puedan llegar a las variables, mientras que antes no expuse a los setters ya que las clases internas tenían acceso directo.
  • Además, las cosas se vuelven un poco más prolijas ya que estoy constantemente llamando a los usuarios en lugar de las variables subyacentes. Una liendre menor, concedida.
  • Los métodos de conveniencia (por ejemplo, checkAssetIsValid () o algo así) necesitan ahora una exposición a nivel de paquete para que las clases auxiliares puedan llamarlos, mientras que antes, como clases internas, podrían ser privados.
  • Peor aún, necesito pasar la clase de implementación del servicio a los constructores de las clases de ayuda porque no quiero exponer estos métodos de ayuda en la interfaz que implementa el servicio porque eso los fuerza a ser públicos. Esto puede crear algunos problemas de prueba / burla unitarios.
  • Peor aún, cualquier sincronización que quisiera hacer se filtra a través de algún método de conveniencia externo (por ejemplo, lockDownAssets () durante una actualización de sondeo). Antes, las clases internas tenían acceso a Bloqueos privados.

    Entonces, en resumen, romper las clases pierde parte de la encapsulación que me gusta. Pero dejarlos en puede dar lugar a algunos archivos grandes de Java. Todavía tengo que encontrar una buena manera de lidiar con esto. C ++ tenía el concepto de "amigos" que rara vez me perdí, pero que en realidad ayudaría en este caso.

    ¿Pensamientos?


  • La línea entre la encapsulación y la separación puede ser difícil de caminar. Sin embargo, creo que el problema principal aquí es que necesitas algún tipo de modelo de interacción sólido para usar como base para separar tus clases.

    Creo que es razonable tener clases de utilidad de ayuda externa que se usan en muchos lugares, siempre que no tengan un efecto secundario, no veo un problema. También es razonable tener clases de ayuda estática, siempre que estén bien organizadas, que contengan métodos utilizados a menudo como checkAssetIsValid (). Esto supone que checkAssetIsValid no necesita acceder a ningún estado externo que no sea el objeto que lo está pasando.

    Lo más importante para la separación es no tener objetos que compartan referencias permanentes en muchas de estas clases. Me gusta buscar programación funcional para obtener orientación. Cada clase no debe estar llegando a las entrañas de otras clases y cambiando de estado. En cambio, cada clase que funciona debe producir y consumir objetos contenedores.

    La visualización también puede ser muy útil. Me di cuenta de un hilo sobre el tema de las herramientas de visualización de Java aquí . Idealmente, su diagrama de interacción de clases debería parecerse más a un árbol que a un gráfico.

    Además, solo quiero señalar que refactorizar una clase grande en clases más pequeñas puede ser extremadamente difícil. Lo mejor es crear un conjunto de pruebas unitarias para la interfaz pública como mínimo, de modo que resulte inmediatamente obvio si se rompe algo. Sé que las pruebas me han ahorrado innumerables horas en el pasado.

    Espero que algo de esto sea útil. Estoy como divagando aquí.


    No olvides considerar por qué estás tratando de separar tu gran clase. ¿Es para fines de ingeniería de software? Por ejemplo, es un punto de acceso a la programación y tienes un archivo tan grande que causa complicadas fusiones en tu equipo de desarrolladores.

    ¿Es solo un deseo general de evitar clases grandes? En ese caso, es posible que dedique mejor su tiempo a mejorar el código que tiene.

    ¿Es el código cada vez más difícil de gestionar, por ejemplo, la eliminación de errores y la prevención de efectos secundarios no deseados se está volviendo cada vez más difícil.

    El comentario de Rick sobre el uso de pruebas unitarias para garantizar un comportamiento continuo y consistente es muy valioso y una buena idea. Puede ser que el diseño actual simplemente impida la refactorización y será mejor que vuelva a implementar el mismo comportamiento a partir de la interfaz del original. ¡Prepárate para un montón de pruebas de regresión!


    Yeap. Probablemente necesites reordenar a esos ayudantes y no moverlos a todos como están. Algunas cosas pertenecen al servicio y otras al ayudante. Probables nuevas clases se deben utilizar para encapsular los datos.

    Una posibilidad que puede usar es que AOP brinde acceso detallado e incluya en el corte de punto que el método solo debe invocarse desde la clase "amigo". Aún así tu método estaría expuesto :(

    Supongo que no hay una solución fácil para esto.


    En las clases internas de nivel de bytecode están simplemente las clases de Java. Como el verificador de bytecode de Java no permite el acceso a miembros privados, genera métodos de acceso sintéticos para cada campo privado que utilice. Además, para vincular la clase interna con su instancia adjunta, el compilador agrega un puntero sintético al ''this'' externo.

    Teniendo esto en cuenta, las clases internas son solo una capa de azúcar sintaxis. Son convenientes y has enumerado algunos puntos buenos, así que enumeraré algunos aspectos negativos que tal vez quieras considerar:

    • Su clase interna tiene una dependencia oculta de toda la clase principal, lo que ofusca su interfaz de entrada. Si lo extrae como clase de paquete privado, tiene la oportunidad de mejorar su diseño y hacerlo más fácil de mantener. Inicialmente es más detallado, pero a menudo encontrarás eso:
      • En lugar de exponer 10 accesores, realmente desea compartir un solo objeto de valor. A menudo, encontrará que realmente no necesita una referencia a toda la clase exterior. Esto también funciona bien con IoC.
      • En lugar de proporcionar métodos para el bloqueo explícito, es más fácil de encapsular la operación con su contexto en una clase separada (o moverla a una de las dos clases, externa o antiguamente, interna).
      • Los métodos de conveniencia pertenecen a las clases de utilidad privada del paquete. Puede usar la importación estática de Java5 para hacer que aparezcan como locales.
    • Su clase externa puede eludir cualquier nivel de protección y acceder directamente a miembros privados de su clase interna. Esto no es malo en sí mismo, pero le quita uno de los medios de lenguaje para expresar su diseño.
    • Dado que su clase interna está integrada en exactamente una clase externa, la única manera de reutilizarla es subclase de la clase externa. Una alternativa sería pasar referencia explícita a una interfaz paquete-privada que implementa la clase externa. Esto le permitiría burlarse del exterior y probar mejor la clase interna.
    • Aunque los depuradores recientes son bastante buenos, he tenido problemas para depurar clases internas antes (confusión de alcance del punto de interrupción condicional, no detenerse en puntos de interrupción, etc.)
    • Las clases privadas inflan su bytecode. Vea mi primer párrafo: a menudo hay una API que podría usar y reducir el número de cruxt sintéticos.

    PD: estoy hablando de clases internas no triviales (especialmente aquellas que no implementan ninguna interfaz). Las implementaciones de tres líneas de escucha son buenas.