¿Cómo encapsular clases internas en una API escrita en Java?

9

Tenemos que escribir una biblioteca. Naturalmente, solo debe tener una API muy pequeña (tan amplia como sea necesario, lo más pequeña posible). Los elementos internos de la biblioteca son algo complejos. Por lo tanto, necesitan estructurarse.

Para la estructuración, actualmente veo dos formas:

1. usar paquetes.

Pros: la biblioteca puede estar perfectamente estructurada. Todo en su lugar.

contras: el uso de clases a través de bordes de paquetes necesita clases públicas y, por lo tanto, amplía la API de toda la biblioteca.

2. use clases internas estáticas, todo en un paquete.

Pros: solo se necesitan muy pocas cosas públicas (clases, métodos, etc.).

Contras: las clases se ocultan solo para estructurarlas. Este sería uno de los pocos casos de uso en los que se utilizan tantas clases internas estáticas. Los desarrolladores no están acostumbrados a eso y podrían pasarlos por alto.


¿Hay una mejor manera de lograr una API pequeña en una biblioteca bien estructurada?

Editar: Olvidé mencionar: es para una biblioteca de Android. Por lo tanto, no java9.

Markus Frauenfelder
fuente
¿Por qué tus clases internas necesitan ser estáticas?
David Arno
¿Las clases internas no hacen que las unidades de código sean más grandes? Quiero decir que una clase con varias clases internas puede llegar a ser muy larga.
Tulains Córdova
2
@ TulainsCórdova, buen punto, leí el término "interno" como "interno", que creo que Java llama "paquete privado". La forma normal de crear una lib es seguramente tener un paquete, que contenga algunas clases públicas, y todo lo demás sea interno / paquete-privado.
David Arno
Hola @frmarkus Traté de volver a escribir el título de su pregunta para ser más específico y evitar los votos cerrados "no está claro lo que está pidiendo". Siéntase libre de volver a editarlo si cree que tergiversa su pregunta real.
Andres F.
1
@ TulainsCórdova: Sí, las unidades de código llegan a ser grandes (3k líneas de código por archivo). Ese es otro problema importante de esta forma de estructuración.
Markus Frauenfelder

Respuestas:

1

Veo lo que está haciendo con 2. Está usando clases como paquetes y paquetes como módulos para que pueda aislarse dentro del paquete pero aún así organizarse dentro del paquete usando clases.

Eso es muy inteligente. Cuidado con lo inteligente.

Esto lo obligará a atascar varias clases en el mismo archivo fuente (que puede preferir) y la ruta tendrá una palabra en mayúscula adicional.

Esto también lo obligará a escribir cualquier código de prueba dentro del paquete a menos que use la reflexión para hackear su entrada desde afuera.

Aparte de eso, esto funcionará. Solo parecerá raro.

Las personas están más acostumbradas a las clases internas que se usan como EntrySet en Hashtable. Es privado, así que no puedo crearlo, pero implementa una interfaz pública, así que solo hablo a través de la interfaz y tengo algo para mí.

Pero estás describiendo clases con las que no quieres que hable incluso a través de una interfaz. Entonces no hay interfaz para mí. Esto significa que no tengo nada que mirar y confundirme (a menos que me proporciones la fuente).

El mayor problema que preveo es que estos novatos confusos mantienen la API. Puede lanzarles documentación y comentarios, pero no se haga demasiado grande cuando no lean o no confíen en ninguno de ellos.

Has creado otro patrón que compensa una deficiencia en el lenguaje. Java no tiene un modificador de acceso que otorgue acceso a un grupo de paquetes. Escuché que se había propuesto un modificador de acceso "módulo", pero no veo signos de que ocurra.

El modificador de acceso predeterminado (sin modificador) es probablemente lo que usará aquí, a menos que no le importe que me filtre por herencia, en cuyo caso protegido.

Modifier        Class     Package   Subclass  World
public          Y         Y         Y         Y
protected       Y         Y         Y         N
no modifier     Y         Y         N         N
private         Y         N         N         N 

Lo que realmente quieres es el acceso al módulo. De esa manera, podría mantener sus pruebas en un paquete y el código en otro. Lamentablemente no lo tenemos en Java.

La mayoría de las personas solo hacen 1 y expanden la API. El uso adecuado de las interfaces mantiene la presión fuera de la implementación.

Hackear lo que quieres en 1 es aún más feo. Echa un vistazo a la pila de llamadas y lanza una excepción cuando lo que te llamó es de un paquete que no te gusta. Eeew

naranja confitada
fuente
3

Por supuesto, separe su código en paquetes. Ayudará mucho a mejorar la mantenibilidad.

Para evitar que los usuarios finales accedan a cosas que no deberían, debe separar su API en la interfaz y la implementación.

Puede hacerlo literalmente, definiendo toda la API en términos de interfaces Java y proporcionando cierto número de clases de fábrica para producir objetos de implementación. Esto es lo que hace JDBC.

O puede hacerlo por convención, creando internalpaquetes y documentando esos paquetes como dependientes de la implementación y sujetos a cambios sin previo aviso o compatibilidad con versiones anteriores.

kdgregory
fuente
1
Desafortunadamente, este es un completo no ir. Esto deja a muchas clases públicas. Pueden tener pistas (siendo fábricas o 'internas'). Pero tengo que aplicarles herramientas adicionales (a toda la clase pública), esto simplemente no es bueno.
Markus Frauenfelder
@frmarkus: creo que (1) espera demasiado de los controles de acceso, o (2) necesita repensar la estructura de su paquete para que las clases de un paquete no tengan que depender de las clases de otro paquete (si puede mantener todo como clases anidadas estáticas, esto no debería ser difícil).
kdgregory