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 ):
Consejos para elegir un nivel de acceso:
Si otros programadores usan tu clase, debes asegurarte de que no puedan ocurrir errores por mal uso. Los niveles de acceso pueden ayudarlo a hacer esto.
- Utilice el nivel de acceso más restrictivo que tenga sentido para un miembro en particular. Use privado a menos que tenga una buena razón para no hacerlo.
- Evite los campos públicos excepto las constantes. (Muchos de los ejemplos en el tutorial usan campos públicos. Esto puede ayudar a ilustrar algunos puntos de manera concisa, pero no se recomienda para el código de producción). Los campos públicos tienden a vincularlo a una implementación particular y limitan su flexibilidad para cambiar su código.
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?
Por supuesto, puede tener métodos privados y, por supuesto, puede probarlos.
O hay alguna forma de hacer que el método privado se ejecute, en cuyo caso puede probarlo de esa manera, o no hay forma de hacer que el privado se ejecute, en cuyo caso: ¿por qué diablos está tratando de probarlo? eliminar la maldita cosa ...
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 ) :
Debe agrupar estas clases y la interfaz en un paquete por varias razones, incluidas las siguientes:
- Usted y otros programadores pueden determinar fácilmente que estos tipos están relacionados ...
- Puede permitir que los tipos dentro del paquete tengan acceso sin restricciones entre sí y aún así restringir el acceso para los tipos fuera del paquete ...
<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.
void nonPrivateMethod(boolean condition) {
if (condition) {
privateMethod();
}
// other code...
}
// unit tests don't invoke nonPrivateMethod(true)
// => privateMethod isn't covered.
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é probar nonPrivateMethod(true)
. No importa lo que haga privateMethod
, 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>