oop - Privado vs protegido: preocupación por buenas prácticas de visibilidad
language-agnostic coding-style (6)
Bueno, todo se trata de encapsulamiento si las clases de paybill manejan la facturación, entonces en la clase de producto, ¿por qué necesitaría todo el proceso de facturación, es decir, el método de pago, cómo pagar dónde pagar ..., permitiendo solo lo que se usa para otras clases y objetos? nada más que ese público para aquellos que otras clases usarían también, protegido para esos límites solo para extender clases. Como eres madara uchiha, lo privado es como "limboo"
, puedes verlo (solo clases de una sola clase).
He estado buscando y sé la diferencia teórica.
- public - Cualquier clase / función puede acceder al método / propiedad.
- protected - Solo esta clase y cualquier subclase pueden acceder al método / propiedad.
- privado : solo esta clase puede acceder al método / propiedad. Ni siquiera será heredado.
Eso está bien y bueno, la pregunta es, ¿cuál es la diferencia práctica entre ellos? ¿Cuándo usarías private
y cuándo lo usarías protected
? ¿Hay una buena práctica estándar o aceptable sobre esta?
Hasta ahora, para mantener el concepto de herencia y polimorfismo, utilizo el public
para todo lo que se debe acceder desde el exterior (como constructores y funcionalidad de clase principal) y protected
para métodos internos (lógica, métodos auxiliares, etc.). ¿Estoy en el camino correcto?
(Tenga en cuenta que esta pregunta es para mí, pero también para referencia futura ya que no he visto una pregunta como esta SO).
Hace un tiempo leí un artículo que hablaba de cerrar todas las clases lo más posible. Haga que todo sea definitivo y privado a menos que tenga una necesidad inmediata de exponer algunos datos o funcionalidades al mundo exterior. Siempre es fácil ampliar el alcance para que sea más permisible más adelante, pero no al revés. Primero considere hacer tantas cosas como sea posible, lo que hará que elegir entre private
y protected
sea mucho más fácil.
- Haz que todas las clases sean definitivas a menos que necesites subclasificarlas de inmediato.
- Haga que todos los métodos sean definitivos, a menos que deba subclasificarlos y anularlos de inmediato.
- Haz que todos los parámetros del método sean definitivos, a menos que necesites cambiarlos dentro del cuerpo del método, que de todos modos es un poco incómodo la mayoría de las veces.
Ahora, si te dejan una clase final, entonces haz que todo sea privado a menos que el mundo lo necesite por completo, hazlo público.
Si te queda una clase que tiene subclase (s), entonces examina cuidadosamente cada propiedad y método. Primero considere si desea exponer esa propiedad / método a las subclases. Si lo hace, considere si una subclase puede causar estragos en su objeto si arruina el valor de la propiedad o la implementación del método en el proceso de anulación. Si es posible, y quieres proteger la propiedad / método de tu clase incluso desde subclases (suena irónico, lo sé), entonces hazlo en privado. De lo contrario, hazlo protegido.
Descargo de responsabilidad: no programo mucho en Java :)
No, no estás en el camino correcto. Una buena regla empírica es: hacer todo lo más privado posible. Esto hace que su clase sea más encapsulada, y permite cambiar las partes internas de la clase sin afectar el código que usa su clase.
Si diseñas tu clase para que sea heredable, entonces eliges cuidadosamente lo que puede ser anulado y accesible desde las subclases, y haz que esté protegido (y finalmente hablando de Java, si quieres que sea accesible pero no invalidable). Pero tenga en cuenta que, tan pronto como acepte tener subclases de su clase, y haya un campo o método protegido, este campo o método es parte de la API pública de la clase y no podrá cambiarse más adelante sin romper las subclases.
Una clase que no está destinada a ser heredada debe hacerse definitiva (en Java). Puede relajar algunas reglas de acceso (privado a protegido, final a no final) por el bien de las pruebas unitarias, pero luego documentarlo y dejar en claro que, aunque el método está protegido, no se debe anular.
Permítanme comenzar con esto diciendo que estoy hablando principalmente sobre el acceso a métodos aquí, y en un grado ligeramente menor, marcando clases finales, no acceso de miembros.
La vieja sabiduría
"márcala como privada a menos que tengas una buena razón para no hacerlo"
tenía sentido en los días en que se escribió, antes de que el código abierto dominara el espacio de la biblioteca del desarrollador y VCS / dependency mgmt. se convirtió en hiper colaborativa gracias a Github, Maven, etc. En ese entonces también se podía ganar dinero limitando los modos en los que se podía utilizar una biblioteca. Pasé probablemente los primeros 8 o 9 años de mi carrera siguiendo estrictamente esta "mejor práctica".
Hoy, creo que es un mal consejo. A veces hay un argumento razonable para marcar un método privado, o una clase final, pero es extremadamente raro, e incluso entonces probablemente no está mejorando nada.
Alguna vez has:
- ¿Ha sido decepcionado / sorprendido / herido por una biblioteca, etc. que tenía un error que podría haberse arreglado con la herencia y algunas líneas de código, pero se vio obligado a esperar un parche oficial debido a métodos / clases privadas / finales?
- ¿Quería utilizar una biblioteca para un caso de uso ligeramente diferente de lo que imaginaron los autores, pero no pudo hacerlo debido a los métodos / clases privadas / finales?
- ¿Ha sido decepcionado / sorprendido / herido por una biblioteca, etc. que fue demasiado permisivo en su extensibilidad?
Estas son las tres mayores racionalizaciones que he escuchado para marcar métodos privados por defecto:
Racionalización n. ° 1: no es seguro y no hay ninguna razón imaginable para anular un método específico
No puedo contar la cantidad de veces que me he equivocado sobre si alguna vez habrá necesidad de anular un método específico que he escrito. Después de haber trabajado en varias librerías populares de código abierto, aprendí por las malas el verdadero costo de marcar las cosas en privado. A menudo elimina la única solución práctica para problemas imprevistos o casos de uso. Por el contrario, nunca en más de 16 años de desarrollo profesional lamenté marcar un método protegido en lugar de privado por razones relacionadas con la seguridad de API. Cuando un desarrollador opta por extender una clase y anular un método, dicen conscientemente "Sé lo que estoy haciendo". y por el bien de la productividad, eso debería ser suficiente. período. Si es peligroso, anótelo en la clase / método Javadocs, no solo cierra la puerta ciegamente.
Marcar los métodos protegidos por defecto es una mitigación para uno de los principales problemas en el desarrollo de SW moderno: el fracaso de la imaginación.
Racionalización n.º 2: mantiene limpias las API / Javadocs públicas
Este es realmente bastante razonable, y dependiendo de la audiencia objetivo, incluso podría ser lo correcto, pero vale la pena considerar cuál es el costo de mantener la API "limpia" en realidad: extensibilidad. Por las razones mencionadas anteriormente, aún podría tener sentido marcar cosas protegidas por defecto por si acaso.
Racionalización n.º 3: mi software es comercial y necesito restringir su uso.
Este es realmente bastante razonable. Como consumidor, iría con el competidor menos restrictivo (suponiendo que no existan diferencias significativas de calidad) cada vez.
Nunca digas nunca
No digo que nunca marques los métodos privados. Estoy afirmando que una mejor regla general es "proteger los métodos a menos que haya una buena razón para no hacerlo".
Este consejo es el más adecuado para quienes trabajan en bibliotecas o proyectos a mayor escala que se han dividido en módulos. Para proyectos más pequeños o más monolíticos, no suele ser tan importante ya que controlas todo el código de todos modos y es fácil cambiar el nivel de acceso de tu código cuando lo necesites. Incluso entonces, todavía daría el mismo consejo :-)
¡Deja de abusar de los campos privados!
Los comentarios aquí parecen ser abrumadoramente de apoyo hacia el uso de campos privados. Bueno, entonces tengo algo diferente que decir.
¿Son los campos privados buenos en principio? Sí. ¡Pero decir que una regla de oro es hacer que todo sea privado cuando no estás seguro es definitivamente incorrecto ! No verá el problema hasta que se encuentre con uno. En mi opinión, debe marcar los campos como protegidos si no está seguro .
Hay dos casos en los que desea extender una clase:
- Desea agregar funcionalidad adicional a una clase base
- Desea modificar la clase existente que está fuera del paquete actual (quizás en algunas bibliotecas)
No hay nada malo con los campos privados en el primer caso. El hecho de que las personas estén abusando de los campos privados lo hace tan frustrante cuando descubres que no puedes modificar una mierda.
Considere una biblioteca simple que modela autos:
class Car {
private screw;
public assembleCar() {
screw.install();
};
private putScrewsTogether() {
...
};
}
El autor de la biblioteca pensó: no hay ninguna razón para que los usuarios de mi biblioteca necesiten acceder a los detalles de implementación de assembleCar()
¿verdad? Marquemos el tornillo como privado.
Bueno, el autor está equivocado. Si desea modificar solo el método assembleCar()
sin copiar toda la clase en su paquete, no tiene suerte. Tienes que reescribir tu propio campo de screw
. Digamos que este automóvil usa una docena de tornillos, y cada uno de ellos involucra un código de inicialización no trivial en diferentes métodos privados, y estos tornillos están marcados como privados. En este punto, comienza a chupar.
Sí, puedes discutir conmigo que el autor de la biblioteca podría haber escrito un código mejor para que no haya nada de malo en los campos privados . No estoy argumentando que el campo privado sea un problema con OOP . Es un problema cuando las personas los están usando.
La moraleja de la historia es que, si estás escribiendo una biblioteca, nunca sabes si tus usuarios quieren acceder a un campo en particular. Si no está seguro, márquelo protected
para que todos sean más felices más adelante. Al menos no abuse del campo privado .
Estoy muy de acuerdo con la respuesta de Nick.
¿Cuándo usarías private
y cuándo lo usarías protected
?
La herencia privada se puede considerar implementada en términos de relación en lugar de una relación IS-A . En pocas palabras, la interfaz externa de la clase heredada no tiene ninguna relación (visible) con la clase heredada. Utiliza la herencia private
solo para implementar una funcionalidad similar que proporciona la clase Base.
A diferencia de la herencia privada, la herencia protegida es una forma restringida de herencia, en la que el tipo IS-A de la clase base deriva de la clase Base y quiere restringir el acceso de los miembros derivados solo a la clase derivada.