¿Es útil el alcance del nivel de paquete Java?

11

Entiendo la idea del alcance del paquete y, a veces, incluso pensé que lo quería. Sin embargo, cada vez que me establezco con la intención seria de intentar usarlo, descubro que no se ajusta a las necesidades que pensé que serviría.

Mi problema principal siempre parece ser que las cosas que deseo limitar el alcance nunca están en el mismo paquete. Conceptualmente, todos pueden estar vinculados, pero la división lógica de los datos dentro de la aplicación los tiene como paquetes secundarios separados de un paquete más grande.

Por ejemplo, puedo tener un modelo Mission y solo quiero que otras herramientas de Mission, como mis MissionServices, utilicen algunos métodos. Sin embargo, termino con Missions.models y Missions.services como mis paquetes, por lo que MissionModel y MissionService no son el mismo alcance de paquete. Nunca parece que haya una situación en la que los paquetes contengan adecuadamente las cosas que me gustaría tener permisos elevados sin incluir también muchas cosas que no deseo tener esos permisos; y rara vez siento que la ventaja del alcance de un paquete justifica la modificación de la arquitectura de mi proyecto para colocar todo en el mismo paquete. A menudo, Aspectos o algún tipo de inversión de control resultan ser el mejor enfoque para cualquier problema que considere brevemente el alcance del paquete.

Tengo curiosidad por decir que esto generalmente se considera cierto en todos los desarrolladores de Java, o es solo una casualidad del trabajo que hago. ¿Se utiliza mucho el alcance del paquete en el mundo real? ¿Hay muchos casos en los que se considera una buena forma de usar, o se ve principalmente como un comportamiento heredado que rara vez se explota en el desarrollo moderno?

No estoy preguntando nada acerca de por qué el ámbito privado del paquete es predeterminado, estoy preguntando cuándo se debe usar independientemente de los valores predeterminados. La mayoría de las discusiones sobre por qué es predeterminado no entran realmente en cuenta cuando el alcance del paquete es realmente útil, sino que simplemente argumentan por qué los otros dos ámbitos comúnmente utilizados no deberían ser predeterminados para que el paquete gane por proceso de eliminación. Además, mi pregunta es sobre el estado actual de desarrollo. Específicamente, nos hemos desarrollado hasta el punto en que otras herramientas y paradigmas hacen que el alcance del paquete sea menos útil que cuando la decisión de hacerlo predeterminado tenía sentido.

dsollen
fuente
Experimento de pensamiento: ¿cómo serían los programas Java si no tuvieras paquetes? Puede ser que no haya mirado o trabajado en ningún programa Java grande .
Robert Harvey
Mira el código para java.util.Stringy java.util.Integer.
2
La respuesta más votada sobre la pregunta duplicada cubre el uso más importante para package-private, respondiendo implícitamente por qué es útil.
2
No estoy del todo convencido por el duplicado. Esta pregunta es "¿Para qué sirve el alcance del paquete?" mientras el otro pregunta (entre otras cosas) "Dado que el alcance del paquete es el predeterminado, ¿para qué sirve el alcance privado?" Además, la respuesta aceptada en el engaño y la respuesta aceptada aquí están haciendo puntos completamente diferentes.
Ixrec

Respuestas:

2

En todo el java.util.*paquete hay instancias en las que el código se escribe con protección a nivel de paquete. Por ejemplo, este bit de java.util.String- un constructor en Java 6 :

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

o este método getChars :

/** Copy characters from this string into dst starting at dstBegin. 
    This method doesn't perform any range checking. */
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, offset, dst, dstBegin, count);
}

La razón de esto es que los diseñadores del código (y java.util.*se puede considerar como una biblioteca bastante grande) querían la capacidad de tener un rendimiento más rápido a costa de la pérdida de varias medidas de seguridad (comprobaciones de rango en matrices, acceso directo a otros campos) que implican la implementación en lugar de la interfaz, el acceso 'inseguro' a partes de la clase que de otro modo se consideran inmutables a través de métodos públicos).

Al restringir el acceso a estos métodos y campos solo por otras clases en el java.utilpaquete, se logra un acoplamiento más estrecho entre ellos, pero también se evita exponer estos detalles de implementación al mundo.

Si está trabajando en una aplicación y no necesita preocuparse por otros usuarios, la protección a nivel de paquete no es algo de lo que deba preocuparse. Aparte de varios aspectos de la pureza del diseño, podría hacer todo publicy estar de acuerdo con eso.

Sin embargo, si está trabajando en una biblioteca que será utilizada por otros (o desea evitar enredar demasiado sus clases para una refactorización posterior), debe usar el nivel de protección más estricto que se le brinde para sus necesidades. A menudo esto significa private. A veces, sin embargo, debe exponer un poco de los detalles de implementación a otras clases en el mismo paquete para evitar repetirse o facilitar un poco la implementación real a expensas del acoplamiento de las dos clases y sus implementaciones. Por lo tanto, el nivel de protección del paquete.


fuente
1
Miré a través de java.util y veo eso. Sin embargo, también noto que es una biblioteca GRANDE. Hoy en día parece que las bibliotecas tan grandes rara vez se escriben sin usar subpaquetes; y son esas divisiones de subpaquete las que llevan a límites. No digo que java.util esté mal, pero parece que podrían salirse con la suya con un paquete grande solo porque tenían EXTREMADAMENTE buenos programadores en los que podían confiar para hacer todo bien con una encapsulación bastante limitada dentro de un paquete; Me sentiría desnudo confiando en ese potencial para hacer mal a los desarrolladores promedio que pueden trabajar en paquetes similares a los míos.
dsollen
1
@dsollen Si profundiza en Spring, e Hibernate o en bibliotecas grandes similares, encontrará cosas similares. Hay momentos en los que necesita el acoplamiento más cercano. Uno debería tener la oportunidad de revisar el código de las presentaciones y asegurarse de que todo sea correcto. Si no necesita el acoplamiento más estrecho, o no confía en los demás codificadores, entonces los campos públicos, los paquetes o los campos o métodos protegidos no siempre son apropiados. Sin embargo, eso no significa que no sea útil.
en resumen, estaría tentado a hacer paquetes más restringidos y no preocuparme por el nivel de optimización (que es solo un desperdicio en el 95% de los proyectos) para mis necesidades cotidianas. Por lo tanto, mi pregunta de seguimiento es: ¿el alcance del nivel de paquete es un potencial de optimización muy específico utilizado solo en ciertas API masivas ampliamente utilizadas? ¿Es decir que solo los grandes necesitarán, o desearán, relajar las protecciones en ese grado? ¿Hay razones por las que los buenos programadores que trabajan incluso en API 'estándar' con bases de usuarios más pequeñas encontrarían uso para esto?
dsollen
No recibí mi publicación de seguimiento lo suficientemente rápido :) Entiendo y veo el punto que hizo, no lo estoy debatiendo en absoluto. Más que simplemente plantear una pregunta de seguimiento en cuanto a su utilidad para el resto de nosotros, buenos programadores, pero que no funcionan en API tan grandes. Específicamente, se trata principalmente de una optimización a expensas de la compensación de encapsulación que se realiza solo cuando realmente necesita ajustar el rendimiento.
dsollen
2
@dsollen no se trata de rendimiento (aunque esa es una razón para exponer la implementación) sino de encapsulación. Lo hace para evitar repetir el código Integer.toString()y String.valueOf(int)puede compartirlo sin exponerlo al mundo. Las java.utilclases deben trabajar en concierto para proporcionar un conjunto más amplio de funcionalidad en lugar de ser clases individuales que son completamente islas en sí mismas: están juntas en un paquete y comparten la funcionalidad de implementación a través del alcance a nivel de paquete.
5

En algún momento, escalo la visibilidad de los métodos privados o protegidos para empaquetar para permitir que una prueba junit acceda a algunos detalles de implementación a los que no se debe acceder desde el exterior.

dado que junit-test tiene el mismo paquete que item-under-test, puede acceder a estos detalles de implementación

ejemplo

  public class MyClass {
    public MyClass() {
        this(new MyDependency());
    }

    /* package level to allow junit-tests to insert mock/fake implementations */ 
    MyClass(IDependency someDepenency) {...}
  }

Es discutible si este es un "buen" uso o no, pero es pragmático

k3b
fuente
2
Siempre pongo las pruebas en un paquete diferente, de lo contrario me parece que dejé algún método clave en el alcance predeterminado que no permite que nadie más lo llame ...
soru
Nunca lo hago con métodos, pero es una buena característica para usar para dependencias inyectadas. Por ejemplo, cuando se prueban EJB, los EntityManagers inyectados se pueden burlar configurando un EM de nivel de paquete con un objeto Mock donde el servidor de aplicaciones inyectaría en tiempo de ejecución.
Jwent
¿Por qué elevarse protectedal alcance del paquete? protected Ya suministra acceso a paquetes. Es un poco extraño, pero creo que no querían exigir declaraciones de alcance compuesto como protected packagescope void doSomething()(que también requiere una nueva palabra clave).
tgm1024 - Mónica fue maltratada el
2

No es tan útil, especialmente por defecto, en un mundo donde las personas tienden a usar los nombres punteados de los paquetes como si fueran subpaquetes. Debido a que Java no tiene un subpaquete, entonces xyinternals no tiene más acceso a xy que a ab Los nombres de paquete son iguales o diferentes, una coincidencia parcial no es diferente de no tener nada en común.

Supongo que los desarrolladores originales nunca esperaron que se usara esa convención, y quisieron mantener las cosas simples sin agregar la complejidad de los paquetes jerárquicos de estilo Ada.

Java 9 está abordando esto, ya que puede colocar varios paquetes en un módulo y solo exportar algunos. Pero eso hará que el alcance del paquete sea aún más redundante.

En un mundo ideal, cambiarían el alcance predeterminado a 'alcance del módulo, si existe un módulo contenedor, de lo contrario, alcance del paquete'. Luego puede usarlo para compartir la implementación dentro de un módulo, lo que permite refactorizar sin romper las interfaces exportadas. Pero tal vez haya alguna sutileza por la que eso rompería la compatibilidad con versiones anteriores o sería malo.

soru
fuente
El comentario de Java 9 me parece interesante. Creo que una capacidad simple de reconocer heraquías de paquetes y de alguna manera definir el alcance del paquete en relación con algún paquete padre (¿usando anotaciones?) Sería bueno.
dsollen
1
@dsollen, tal vez, pero mi primera inclinación es que comenzaríamos a dorar los neumáticos con esa. En mi opinión, un primer paso mucho mejor para la claridad (que aporta seguridad en cierta medida) sería inventar una palabra clave de alcance de paquete real. Incluso podría ser "paquete" :)
tgm1024 - Mónica fue maltratada el
2

El tipo de cohesión que describe no es realmente el mejor ejemplo de dónde usaría el acceso a paquetes. El paquete es útil para crear componentes, módulos que son intercambiables en una interfaz.

Aquí hay un ejemplo aproximado de un escenario. Supongamos que su código se reorganizó para estar más relacionado con la cohesión funcional y la componente. Quizás 3 frascos como:

**MissionManager.jar** - an application which uses the Java Service Provider Interface to load some implementation of missions, possible provided by a 3rd party vendor.

**missions-api.jar** - All interfaces, for services and model
    com...missions
        MissionBuilder.java   - has a createMission method
        Mission.java - interface for a mission domain object
    com...missions.strategy
        ... interfaces that attach strategies to missions ...
    com...missions.reports
        .... interfaces todo with mission reports ....

   **MCo-missionizer-5000.jar** -  provides MCo's Missionizer 5000 brand mission implementation.
       com...missions.mco
                  Mission5000.java - implements the Mission interface.
       com...missions.strategy.mco
              ... strategy stuff etc ...

Al usar el nivel de paquete en el Mission5000.java, puede estar seguro de que ningún otro paquete o componente como el MissionManager puede ajustarse estrechamente a la implementación de la misión. Ese es solo el componente provisto por "terceros" de "M co" en realidad crea su propia implementación especial de marca de una Misión. El administrador de la misión debe llamar a MissionBuiler.createMission (...) y usar la interfaz definida.

De esta manera, si se reemplaza el componente de implementación de Mission, sabemos que los implementadores de MissionManager.jar no omitieron la API y usaron la implementación de MCo directamente.

Por lo tanto, podemos intercambiar MCo-missionizer5000.jar con un producto de la competencia. (O tal vez un Simulacro para la unidad que prueba al Gerente de la Misión)

Por el contrario, MCo puede esconder sus secretos comerciales de misioneros patentados.

(Esto también está relacionado con la aplicación de ideas de inestabilidad y abstracción en los componentes).

Chris
fuente
-1

Debido a que el alcance del paquete en Java no está diseñado de forma jerárquica, no es útil. No usamos el alcance del paquete en absoluto y definimos todas las clases como públicas.

En su lugar, utilizamos nuestras propias reglas de acceso basadas en la jerarquía de paquetes y los nombres de paquetes predefinidos.

Si está interesado en los detalles de google para "CODERU-Rules".

Alexander Rubinov
fuente