Estoy haciendo esta pregunta porque creo que lo hicieron por una muy buena razón y que la mayoría de las personas no lo usan correctamente, de todos modos, según mi experiencia en la industria hasta ahora. Pero si mi teoría es cierta, entonces no estoy seguro de por qué incluyeron el modificador de acceso privado ...
Creo que si el acceso predeterminado se usa correctamente, proporciona una capacidad de prueba mejorada mientras se mantiene la encapsulación. Y también hace que el modificador de acceso privado sea redundante.
El modificador de acceso predeterminado se puede usar para proporcionar el mismo efecto mediante el uso de un paquete único para métodos que deben ocultarse del resto del mundo, y lo hace sin comprometer la capacidad de prueba, ya que los paquetes en una carpeta de prueba, con el mismo capaz de acceder a todos los métodos predeterminados declarados en una carpeta de origen.
Creo que es por eso que Java usa el acceso a paquetes como 'predeterminado'. Pero no estoy seguro de por qué también incluyeron acceso privado, estoy seguro de que hay un caso de uso válido ...
Respuestas:
Supongo que tenían una buena idea de lo que haría un programador promedio. Y por programador promedio me refiero a uno que no es realmente bueno en programación, pero que consigue el trabajo de todos modos porque no hay suficientes buenos y son caros.
Si hacen que el acceso "público" sea el predeterminado, la mayoría de los programadores nunca se molestarán en usar otra cosa. El resultado sería una gran cantidad de código de espagueti en todas partes. (Porque si técnicamente se le permite llamar a cualquier cosa desde cualquier lugar, ¿por qué molestarse en encapsular alguna lógica dentro de un método?)
Hacer "privado" el acceso predeterminado solo sería un poco mejor. La mayoría de los programadores principiantes simplemente inventarían (y compartirían en sus blogs) una regla general que dice "necesitas escribir 'público' en todas partes", y luego se quejarían de por qué Java es tan malo que los obliga a escribir "público" en todas partes. Y también producirían mucho código de espagueti.
El acceso al paquete es algo que permite a los programadores usar sus técnicas de programación descuidadas mientras escriben código dentro de un paquete, pero luego tienen que reconsiderarlo al hacer más paquetes. Es un compromiso entre las buenas prácticas comerciales y la fea realidad. Me gusta: escriba un código de espagueti, si insiste, pero deje el feo desorden dentro del paquete; al menos cree algunas interfaces más agradables entre los paquetes.
Probablemente también haya otras razones, pero no subestimaría esta.
fuente
El acceso predeterminado no hace que el modificador de acceso privado sea redundante.
La posición de los diseñadores de idiomas al respecto se refleja en el tutorial oficial: Control del acceso a los miembros de una clase y es bastante claro (para su conveniencia, declaración relevante en la cita en negrita ):
Su apelación a la capacidad de prueba como justificación para descartar por completo el modificador privado es incorrecta, como lo demuestran, por ejemplo, las respuestas en Nuevo en TDD. ¿Debo evitar los métodos privados ahora?
La posición de los diseñadores de idiomas sobre el propósito y el uso del acceso a nivel de paquete se explica en otro tutorial oficial, Creación y uso de paquetes, y no tiene nada en común con la idea de descartar modificadores privados (para su conveniencia, declaración relevante en la cita en negrita ) :
<despotricar "Creo que escuché suficientes quejidos. Supongo que es hora de decir fuerte y claro ...">
Los métodos privados son beneficiosos para las pruebas unitarias.
La nota a continuación supone que está familiarizado con la cobertura del código . Si no, tómese un tiempo para aprender, ya que es bastante útil para aquellos interesados en las pruebas unitarias y en las pruebas.
Muy bien, entonces tengo ese método privado y pruebas unitarias, y el análisis de cobertura me dice que hay una brecha, mi método privado no está cubierto por las pruebas. Ahora...
¿Qué gano al mantenerlo privado?
Dado que el método es privado, la única forma de proceder es estudiar el código para aprender cómo se usa a través de una API no privada. Por lo general, dicho estudio revela que la razón de la brecha es que falta un escenario de uso particular en las pruebas.
En aras de la integridad, otras razones (menos frecuentes) para tales brechas de cobertura podrían ser errores en la especificación / diseño. No voy a profundizar en esto aquí, para simplificar las cosas; basta con decir que si debilita la limitación de acceso "solo para hacer que el método sea comprobable", perderá la oportunidad de saber que estos errores existen.
Bien, para arreglar la brecha, agrego una prueba unitaria para el escenario faltante, repito el análisis de cobertura y verifico que la brecha haya desaparecido. ¿Qué tengo ahora? Tengo una nueva prueba de unidad para el uso específico de API no privada.
La nueva prueba asegura que el comportamiento esperado para este uso no cambiará sin previo aviso ya que si cambia, la prueba fallará.
Un lector externo puede analizar esta prueba y aprender cómo se supone que debe usar y comportarse (aquí, el lector externo incluye mi futuro yo, ya que tiendo a olvidar el código un mes o dos después de que lo haya terminado).
La nueva prueba es tolerante a la refactorización (¿refactorizo los métodos privados? ¡Apuesto!) Lo que sea que haga
privateMethod
, siempre querré probarnonPrivateMethod(true)
. No importa lo que hagaprivateMethod
, no será necesario modificar la prueba porque el método no se invoca directamente.¿No está mal? Usted apuesta.
¿Qué pierdo al debilitar la limitación de acceso?
Ahora imagine que en lugar de lo anterior, simplemente debilito la limitación de acceso. Me salto el estudio del código que usa el método y procedo directamente con la prueba que invoca mi
exPrivateMethod
. ¿Excelente? ¡No!¿Obtengo una prueba para el uso específico de la API no privada mencionada anteriormente? No: no había una prueba para
nonPrivateMethod(true)
antes, y no hay tal prueba ahora.¿Los lectores externos tienen la oportunidad de comprender mejor el uso de la clase? No. "- Oye, ¿cuál es el propósito del método probado aquí? - Olvídalo, es estrictamente para uso interno. - Oops".
¿Es tolerante refactorizar? De ninguna manera: lo que sea que cambie
exPrivateMethod
, probablemente romperá la prueba. Cambiar el nombre, fusionarse con algún otro método, cambiar los argumentos y la prueba simplemente dejará de compilarse. ¿Dolor de cabeza? Usted apuesta!En resumen , seguir con el método privado me brinda una mejora útil y confiable en las pruebas unitarias. Por el contrario, el debilitamiento de las limitaciones de acceso "para la capacidad de prueba" solo me da una pieza oscura y difícil de entender del código de prueba, que además está en riesgo permanente de ser roto por cualquier refactorización menor; francamente, lo que obtengo se parece sospechosamente a una deuda técnica .
</rant>
fuente
La razón más probable es: tiene que ver con la historia. El antepasado de Java, Oak, solo tenía tres niveles de acceso: privado, protegido, público.
Excepto que privado en Oak era el equivalente de paquete privado en Java. Puede leer la sección 4.10 de la Especificación del idioma de Oak (énfasis mío):
Entonces, según su punto de vista, el acceso privado, como se conoce en Java, no estaba allí originalmente. Pero cuando tiene más de unas pocas clases en un paquete, no tener clases privadas como las conocemos ahora conduciría a una pesadilla de colisión de nombres (por ejemplo, java.until.concurrent tiene cerca de 60 clases), lo cual es probablemente la razón por la cual lo introdujo
Sin embargo, la semántica predeterminada de acceso al paquete (originalmente llamada privada) no se modificó entre Oak y Java.
fuente
Hay situaciones en las que uno desearía dos clases separadas que estén más vinculadas que exponer todo al público. Esto tiene ideas similares de 'amigos' en C ++ donde otra clase que se declara como 'amigo' de otra puede acceder a sus miembros privados.
Si miras las entrañas de clases como BigInteger , encontrará una serie de protección predeterminada de paquetes en campos y métodos (todo lo que es un triángulo azul en la lista). Esto permite que otras clases en el paquete java.math tengan un acceso más óptimo a sus entrañas para operaciones específicas (puede encontrar estos métodos llamados en BigDecimal - BigDecimal está respaldado por un BigInteger y ahorra volver a implementar BigInteger nuevamente . Que estos son niveles de paquete no ' Es un problema de protección porque java.math es un paquete sellado y no se le pueden agregar otras clases.
Por otro lado, hay muchas cosas que de hecho son privadas, como deberían ser. La mayoría de los paquetes no están sellados. Sin privado, su compañero de trabajo podría poner otra clase en ese paquete y acceder al campo privado (ya no) y romper la encapsulación.
Cabe destacar que las pruebas unitarias no eran algo en lo que se pensaba cuando se construían los ámbitos de protección. JUnit (el marco de prueba de XUnit para Java) no se concibió hasta 1997 (una narración de la historia se puede leer en http://www.martinfowler.com/bliki/Xunit.html ). Las versiones Alpha y Beta del JDK fueron en 1995 y JDK 1.0 fue en 1996, aunque los niveles de protección realmente no se concretaron hasta JDK 1.0.2 (antes de eso, podría tener un
private protected
nivel de protección).Algunos argumentarían que el valor predeterminado no debería ser el nivel del paquete, sino privado. Otros argumentan que no debería haber una protección predeterminada , pero todo debería estar explícitamente establecido; algunos seguidores de esto escribirán código como:
Tenga en cuenta el comentario allí.
La verdadera razón por la cual la protección a nivel de paquete es la predeterminada es probable que se pierda en las notas de diseño de Java (he estado cavando y no puedo encontrar ningún artículo de por qué , mucha gente explica la diferencia, pero ninguno dice "esta es la razón ").
Así que adivinaré. Y eso es todo lo que es, una suposición.
En primer lugar, la gente todavía intentaba descifrar el diseño del lenguaje. Desde entonces, los idiomas han aprendido de los errores y éxitos de Java. Esto podría enumerarse como un error al no tener algo que deba definirse para todos campos.
public
y,private
dado que tienen el mayor impacto en el acceso.Por lo tanto, privado no es predeterminado y bueno, todo lo demás encajó. El alcance predeterminado se dejó como predeterminado. Se puede haber sido la primera ámbito que se ha creado y en el interés de rigurosa compatibilidad con código antiguo, que quedaba de esa manera.
Como dije, todo esto es una suposición .
fuente
La encapsulación es una forma de decir "no tienes que pensar en esto".
Poniendo esto en términos no técnicos.
fuente
No creo que se hayan centrado en las pruebas, simplemente porque las pruebas no eran muy comunes en ese entonces.
Lo que sí creo que querían lograr era la encapsulación en el nivel del paquete. Como sabe, una clase puede tener métodos y campos internos y solo exponer algunos de ellos a través de miembros públicos. Del mismo modo, un paquete puede tener clases internas, métodos y campos, y solo exponer algunos de ellos. Si piensa de esta manera, un paquete tiene una implementación interna en un conjunto de clases, métodos y campos, junto con una interfaz pública en otro conjunto (posiblemente superpuesto) de clases, métodos y campos.
Los codificadores más experimentados que conozco piensan de esta manera. Toman la encapsulación y la aplican a un nivel de abstracción más alto que la clase: un paquete o un componente si lo desea. Me parece razonable que los diseñadores de Java ya fueran tan maduros en sus diseños, considerando lo bien que Java todavía se mantiene.
fuente