Privado vs protegido - Preocupación de buenas prácticas de visibilidad [cerrado]

145

He estado buscando y sé la diferencia teórica.

  • public : cualquier clase / función puede acceder al método / propiedad.
  • protegido : 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 privatey cuándo usarías protected? ¿Existe una buena práctica estándar o aceptable sobre esta?

Hasta ahora, para retener el concepto de herencia y polimorfismo, lo uso publicpara cualquier cosa a la que se deba acceder desde el exterior (como constructores y funcionalidad de clase principal), y protectedpara 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).

El fantasma de Madara
fuente
33
¿Importa? Cualquier idioma con soporte de OOP tiene esta preocupación. Por casualidad programo en PHP, pero creo que la pregunta se aplica a cualquier lenguaje de soporte de OOP.
El fantasma de Madara el
2
Muy bien, me pregunto si te habías olvidado de etiquetar. Ahora veo la etiqueta oop.
Paul Bellora
¿Tengo que establecer la visibilidad (privada / pública / protegida) para todas y cada una de las propiedades de la clase? ¿O solo algunos de ellos necesitan tener un tipo de visibilidad? Si es así, ¿cómo decidir qué propiedad necesita tener una visibilidad establecida en la parte superior de la clase?
user4271704
1
Por casualidad, la diferencia entre protegido y privado parece obvia. Use protegido si las subclases usarán el método / variable, de lo contrario, use privado. Específicamente, si las subclases tendrían que redefinir una variable privada muy similar en el padre, simplemente protéjala.
iPherian
Hay otro especificador de acceso internalen lenguaje C # que restringe el nivel de acceso de una clase o sus miembros dentro de un ensamblaje físico. Sin embargo, no estoy seguro de su soporte o algo similar en otros idiomas.
RBT

Respuestas:

128

No, no estás en el camino correcto. Una buena regla general es: hacer que todo sea lo más privado posible. Esto hace que su clase esté más encapsulada y permite cambiar las partes internas de la clase sin afectar el código que usa su clase.

Si diseña su clase para que sea heredable, elija cuidadosamente lo que puede ser anulado y accesible desde las subclases, y haga que esté protegido (y finalmente, hablando de Java, si desea hacerlo accesible pero no anulable). 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 se puede cambiar más adelante sin romper las subclases.

Una clase que no está destinada a ser heredada debe hacerse final (en Java). Puede relajar algunas reglas de acceso (privado a protegido, final a no final) en aras de las pruebas unitarias, pero luego documentarlo y dejar en claro que, aunque el método está protegido, no se debe anular.

JB Nizet
fuente
1
La verdadera pregunta aquí es sobre privatevs ¿ protectedCuándo quiero que se herede una propiedad y cuándo no? Realmente no puedo decir si un usuario a veces en el futuro quiere tomar mi clase y extenderla ...
Madara's Ghost
16
Bueno, la cuestión no es lo que el usuario quiere reemplazar, la cuestión es lo que quiere permitir que sea anulado. Por lo general, ayuda cambiar de bando e intentar pensar: si usara esa clase y creara una subclase, ¿qué me gustaría poder anular? A veces, otros usuarios todavía extrañarán cosas ...
Thorsten Dittmar
3
¡Los campos protegidos son malas prácticas en herencia! . Por favor ten cuidado con eso. He aquí por qué
RBT
44
¡Los campos privados son malos en la práctica en herencia !. (sobre exagerando) Por favor lea mi respuesta.
Larry
66
Opinión típica de fanáticos del control.
bruno desthuilliers
58

Permítanme comenzar esto diciendo que estoy hablando principalmente sobre el acceso a métodos aquí, y en menor medida, marcando las clases como final, no como acceso de miembros.

La vieja sabiduria

"márquelo como privado a menos que tenga 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 la gestión de VCS / dependencia. se convirtió en hiper colaborativo gracias a Github, Maven, etc. En aquel entonces también se podía ganar dinero restringiendo las formas en que se podía utilizar una biblioteca. Probablemente pasé 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:

  • ¿Se sintió decepcionado, sorprendido o herido por una biblioteca, etc. que tenía un error que podría haberse solucionado con herencia y pocas líneas de código, pero debido a métodos privados / finales y las clases se vieron obligados a esperar un parche oficial que podría no llegar? Yo tengo.
  • ¿Deseaba usar una biblioteca para un caso de uso ligeramente diferente al imaginado por los autores, pero no pudo hacerlo debido a métodos y clases privados / finales? Yo tengo.
  • ¿Te decepcionó, sorprendió o lastimó una biblioteca, etc. que era demasiado permisiva en cuanto a su extensibilidad? Yo no he.

Estas son las tres racionalizaciones más importantes que he escuchado para marcar métodos privados por defecto:

Racionalización # 1: no es seguro y no hay razón para anular un método específico

No puedo contar la cantidad de veces que me he equivocado acerca de si alguna vez será necesario anular un método específico que he escrito. Después de haber trabajado en varias librerías de código abierto populares, aprendí por las malas el costo real de marcar las cosas como privadas. 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 me arrepiento de haber marcado un método protegido en lugar de privado por razones relacionadas con la seguridad API. Cuando un desarrollador elige extender una clase y anular un método, conscientemente dice "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 cierre la puerta a ciegas.

Los métodos de marcado protegidos por defecto son una mitigación para uno de los principales problemas en el desarrollo moderno de SW: falta de imaginación.

Racionalización # 2: mantiene limpias las API públicas / Javadocs

Este es más razonable y, dependiendo del público objetivo, incluso podría ser lo correcto, pero vale la pena considerar cuál es el costo de mantener la API "limpia": la extensibilidad. Por las razones mencionadas anteriormente, probablemente tenga más sentido marcar cosas protegidas por defecto por si acaso.

Racionalización # 3: mi software es comercial y necesito restringir su uso.

Esto también es razonable, pero como consumidor iría con el competidor menos restrictivo (suponiendo que no existan diferencias de calidad significativas) cada vez.

Nunca digas nunca

No digo que nunca marques los métodos como privados. Estoy diciendo que la 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 de mayor escala que se han dividido en módulos. Para proyectos más pequeños o más monolíticos, no tiende a importar tanto ya que usted controla todo el código de todos modos y es fácil cambiar el nivel de acceso de su código si lo necesita. Aun así, todavía daría el mismo consejo :-)

Mella
fuente
Wrt your Rationalization # 1: cuando marco algo como protegido, elijo hacerlo explícitamente, ya que siento que este comportamiento no es definitivo y podría ser un caso en el que las clases de mi hijo querrían anularlo. Si no estoy tan seguro, me gustaría hacerlo privado por defecto. Especialmente en un lenguaje como C # que mantiene un método no virtual de forma predeterminada, agregar solo un especificador de acceso protegido no tiene sentido. Debe agregar ambos virtualy protectedpalabras clave para que su intención sea muy clara. Además, las personas prefieren la asociación a la herencia, por lo protectedque, por defecto, es difícil de percibir
RBT
44
La combinación de palabras clave necesarias para hacer que un método sea reemplazable es un detalle de lenguaje en mi humilde opinión. El argumento contrario que presento para la racionalización # 1 está directamente dirigido al caso que mencionas "Si no estoy tan seguro ...". Hablando en términos prácticos, si no puede pensar en una razón por la cual sería peligroso, entonces se puede ganar más optando por la extensibilidad. Cuando dices 'asociación', estoy tomando eso como sinónimo de composición, en cuyo caso no veo que importe de ninguna manera.
Nick
3
No recuerdo cuántas veces tuve que "clonar" las clases subyacentes solo porque quería anular 1 o 2 o los métodos. Y como dijiste, con la tendencia social de que estamos compartiendo más código que nunca antes, tiene mucho más sentido ir con protegido que privado por defecto. es decir, estar más abierto a sus propias lógicas.
shinkou
1
De acuerdo. Solo quería modificar el BufferedReader de Java para manejar delimitadores de línea personalizados. Gracias a los campos privados, tengo que clonar toda la clase en lugar de simplemente extenderla y anular readLine ().
Ron Dunn
21

¡Deja de abusar de los campos privados!

Los comentarios aquí parecen apoyar abrumadoramente el uso de campos privados. Bueno, entonces tengo algo diferente que decir.

¿Son buenos los campos privados en principio? Si. ¡Pero decir que una regla de oro es hacer que todo sea privado cuando no estés seguro de que definitivamente está mal ! 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 abusen de los campos privados lo hace muy frustrante cuando descubres que no puedes modificar la mierda.

Considere una biblioteca simple que modela automóviles:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

El autor de la biblioteca pensó: no hay razón para que los usuarios de mi biblioteca necesiten acceder a los detalles de implementación de la assembleCar()derecha. Marquemos el tornillo como privado.

Bueno, el autor está equivocado. Si desea modificar solo el assembleCar()método sin copiar toda la clase en su paquete, no tiene suerte. Tienes que reescribir tu propio screwcampo. 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 todos estos tornillos están marcados como privados. En este punto, comienza a apestar.

Sí, puede discutir conmigo que el autor de la biblioteca podría haber escrito un código mejor para que no haya nada malo en los campos privados . No estoy argumentando que el campo privado es un problema con OOP . Es un problema cuando las personas los usan.

La moraleja de la historia es que, si está escribiendo una biblioteca, nunca sabe si sus usuarios desean acceder a un campo en particular. Si no está seguro, márquelo protectedpara que todos estén más felices más tarde. Al menos no abuses del campo privado .

Apoyo mucho la respuesta de Nick.

Larry
fuente
2
El uso de campos privados es principalmente decir que la herencia es la abstracción incorrecta para alterar un cierto comportamiento de una clase / objeto (si se usa conscientemente). La alteración generalmente violará silenciosamente el LSP a medida que se cambie el comportamiento sin que cambie la API. Gran respuesta, +1.
El fantasma de Madara el
¡Predicar! Privado es la ruina de mi existencia cuando intento escribir mods de Minecraft. Nada más molesto que tener que usar Reflection o ASM para arreglar eso.
RecursiveExceptionException
Según entendí la respuesta de Nick, se refería principalmente a métodos, no a propiedades. Estoy totalmente de acuerdo en que no debemos declarar métodos privados cada vez y el uso de esos debe ser cuidadoso, pero por qué aconsejaría declarar propiedades protegidas sobre privadas solo porque desea "reescribir" la clase más fácilmente. Comparta totalmente su frustración ya que estoy trabajando con 3rd p's diariamente, pero alentemos a las personas a crear una arquitectura sólida y no solo diciendo "el código terminaría siendo una mierda, así que protejámoslo desde el principio para que podamos reescribirlo fácilmente". Aunque comparto tu frustración.
sean662
16

Hace un tiempo leí un artículo que hablaba sobre bloquear todas las clases tanto como sea posible. Haga que todo sea final 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 ser más permisible más adelante, pero no al revés. Consideremos en primer lugar hacer tantas cosas como sea posible finallo que hará que la elección entre privatey protectedmucho más fácil.

  1. Haga que todas las clases sean finales a menos que necesite subclasificarlas de inmediato.
  2. Haga que todos los métodos sean finales a menos que necesite subclasificarlos y anularlos de inmediato.
  3. Haga que todos los parámetros del método sean finales a menos que necesite cambiarlos dentro del cuerpo del método, lo cual es un poco incómodo la mayoría de las veces.

Ahora, si te queda una clase final, haz que todo sea privado a menos que el mundo necesite absolutamente algo: hazlo público.

Si te queda una clase que tiene subclase (s), entonces examina cuidadosamente cada propiedad y método. Primero considere si incluso desea exponer esa propiedad / método a 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 desea proteger la propiedad / método de su clase incluso de subclases (suena irónico, lo sé), entonces hágalo privado. De lo contrario, protégelo.

Descargo de responsabilidad: no programo mucho en Java :)

Anurag
fuente
2
Para aclarar: A final classen Java es una clase de la que no se puede heredar. A final methodno es virtual, es decir, no puede ser reemplazado por una subclase (los métodos Java son virtuales por defecto). A final field/parameter/variablees una referencia de variable inmutable (en el sentido de que no se puede modificar después de que se inicializó).
M.Stramm
1
Curiosamente, he visto el consejo opuesto exacto, que nada debe ser definitivo a menos que sea seguro que anular la cosa en cuestión tiene el potencial de romper una invariante. De esa manera, cualquiera es libre de extender sus clases según sea necesario.
GordonM
¿Puede nombrar un caso en su carrera de programación en el que marcar un parámetro de método "final" marcó una diferencia para su comprensión? (distinto al requerido por el compilador)
user949300
7

¿Cuándo usarías privatey cuándo usarías protected?

La herencia privada puede considerarse 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 relación (visible) con la clase heredada. Utiliza la privateherencia 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 la clase derivada es un tipo IS-A de la clase base y quiere restringir el acceso de los miembros derivados solo a la clase derivada.

Alok Save
fuente
2
Que "Implementado en términos de relación" es una relación HAS-A. Si todos los atributos son privados, la única forma de acceder a ellos es a través de métodos. Esto es lo mismo que si la subclase contuviera un objeto de la superclase. Eliminar todos los atributos protegidos de sus clases concretas también significa eliminar toda la herencia de ellos.
shawnhcorey
2
Interesante proceso de pensamiento - Herencia privada . @shawnhcorey, gracias por hacer explícito que apunta hacia la relación HAS-A , también conocida como asociación (que puede ser agregación o composición). Siento que esta publicación también debe actualizarse para aclarar lo mismo.
RBT
1

Bueno, se trata de encapsular si las clases de pago manejan la facturación del pago, entonces en la clase de producto, ¿por qué necesitaría todo el proceso del proceso de facturación, es decir, el método de pago, cómo pagar dónde pagar? nada más que ese público para aquellos donde otras clases también lo usarían, protegidos para esos límites solo para extender clases. Como eres madara uchiha, lo privado es como "limboo"lo puedes ver (solo clasificas en una sola clase).

ujwal dhakal
fuente