¿Es private
útil la visibilidad de los campos de clase / propiedades / atributos? En OOP, tarde o temprano, hará una subclase de una clase y, en ese caso, es bueno comprender y poder modificar la implementación por completo.
Una de las primeras cosas que hago cuando subclasifico una clase es cambiar un montón de private
métodos protected
. Sin embargo, ocultar detalles del mundo exterior es importante, por lo que necesitamos protected
y no solo public
.
Mi pregunta es: ¿Conoces un caso de uso importante en el que, en private
lugar de protected
ser una buena herramienta, o dos opciones " protected
& public
" serían suficientes para los idiomas OOP?
object-oriented
encapsulation
access-modifiers
Adam Libuša
fuente
fuente
Respuestas:
Porque como dices,
protected
aún te deja la capacidad de "modificar la implementación por completo". Realmente no protege nada dentro de la clase.¿Por qué nos importa "proteger genuinamente" las cosas dentro de la clase? Porque de lo contrario sería imposible cambiar los detalles de implementación sin romper el código del cliente . Dicho de otra manera, las personas que escriben subclases también son "el mundo exterior" para la persona que escribió la clase base original.
En la práctica, los
protected
miembros son esencialmente una "API pública para subclases" de una clase y deben permanecer estables y compatibles con versiones anteriores tanto como lospublic
miembros. Si no tuviéramos la capacidad de crearprivate
miembros verdaderos , entonces nada en una implementación sería seguro de cambiar, porque no podría descartar la posibilidad de que el código del cliente (no malicioso) haya logrado depender de alguna manera eso.Por cierto, aunque "en OOP, tarde o temprano, vas a hacer una subclase de una clase" es técnicamente cierto, tu argumento parece estar asumiendo mucho más fuerte que "tarde o temprano, vas a hacer una subclase de cada clase ", lo que casi seguro no es el caso.
fuente
private
que tiene sentido para mí. La perspectiva lib debe usarse con más frecuencia, de lo contrario, cualquier persona aficionada a la mentalidad de codificación de "control absoluto, responsabilidad absoluta" (tipo C) podría señalarla como "me protege de mí mismo". Tenga en cuenta que todavía lo usabaprivate
anteriormente, siempre sentí una buena documentación y una convención de nomenclatura como_foo
para indicar que probablemente no debería meterse con ella era equivalente, si no mejor. Ser capaz de decir determinísticamente "nada se romperá" es una característica legítima yprivate
exclusiva.Esto está mal. No todas las clases están destinadas a ser subclasificadas y algunos lenguajes OOP de tipo estático incluso tienen características para evitarlo, por ejemplo,
final
(Java y C ++) osealed
(C #).No, no es. Es bueno que una clase pueda definir claramente su interfaz pública y preservar sus invariantes incluso si se hereda de ella.
En general, el control de acceso se trata de compartimentación. Desea que se entienda una parte individual del código sin tener que comprender en detalle cómo interactúa con el resto del código. El acceso privado lo permite. Si al menos todo está protegido, debe comprender lo que hace cada subclase para comprender cómo funciona la clase base.
O para ponerlo en los términos de Scott Meyers: las partes privadas de una clase se ven afectadas por una cantidad finita de código: el código de la clase misma.
Las partes públicas están potencialmente afectadas por cada bit de código existente y cada bit de código aún por escribir, que es una cantidad infinita de código.
Las partes protegidas se ven potencialmente afectadas por cada subclase existente, y cada subclase aún por escribir, que también es una cantidad infinita de código.
La conclusión es que protegido le brinda muy poco más que público, mientras que privado le brinda una mejora real. Es la existencia del especificador de acceso protegido lo que es cuestionable, no privado.
fuente
virtual
, por las razones descritas en esta respuesta.protected
es para métodos, no para miembros de datos.sealed
partes grandes de la biblioteca de .net y IIRC por qué le hubiera gustado encerrar el resto. En el momento en que permita que los herederos de terceros comiencen a tocar valores internos, debe agregar una gran cantidad de validaciones / comprobaciones de validez a cada método porque ya no puede confiar en ningún diseño invariante sobre el estado interno de los objetos.Sí, los campos privados son absolutamente necesarios. Justo esta semana, necesitaba escribir una implementación de diccionario personalizada donde controlara lo que se puso en el diccionario. Si el campo del diccionario se hiciera protegido o público, entonces los controles que había escrito tan cuidadosamente podrían haberse eludido fácilmente.
Los campos privados generalmente tratan de proporcionar garantías de que los datos son como el codificador original esperaba. Haga que todo esté protegido / público y monte un entrenador y caballos a través de esos procedimientos y validación.
fuente
private
es solo una forma de decir "no tocar estos" y hacer que se cumpla en el contrato del compilador. En sistemas como .NET, esto también tiene importantes implicaciones de seguridad: solo puede tocar las partes privadas de otros cuando tiene plena confianza (básicamente el equivalente al acceso de administrador / root).Cuando se intenta razonar formalmente sobre la corrección de un programa orientado a objetos , es típico utilizar un enfoque modular que involucra invariantes de objetos . En este enfoque
El razonamiento modular sobre un objeto procede de la siguiente manera (al menos una aproximación al menos)
Imagine que verificamos y utilizamos
object A
el enfoque anterior. Y ahora desea verificarmethod g
deobject B
que se pidemethod f
deobject A
. El razonamiento modular nos permite razonarmethod g
sin tener que reconsiderar la implementación demethod f
. Siempre que podamos establecer la invarianteobject A
y la precondiciónmethod f
en el sitio de la llamada,method g,
podemos tomar la condición posteriormethod f
como un resumen del comportamiento de la llamada al método. Además, también sabremos que después de que la llamada regresa, la invariante deA
todavía se mantiene.Esta modularidad de razonamiento es lo que nos permite pensar formalmente sobre programas grandes. Podemos razonar sobre cada uno de los métodos individualmente y luego componer los resultados de este razonamiento a su vez para razonar sobre partes más grandes del programa.
Los campos privados son muy útiles en este proceso. Para saber que la invariante de un objeto continúa reteniéndose entre dos llamadas a métodos en ese objeto, generalmente confiamos en el hecho de que el objeto no se modifica en el período intermedio.
Para que el razonamiento modular funcione en un contexto en el que los objetos no tienen campos privados, entonces tendríamos que tener alguna forma de asegurarnos de que lo que sea que un campo sea configurado por otro objeto, que el invariante siempre se restablezca (después del campo conjunto). Es difícil imaginar un objeto invariable que se mantenga sin importar el valor que tengan los campos del objeto, y que también sea útil para razonar sobre la corrección del programa. Probablemente tendríamos que inventar alguna convención complicada sobre el acceso al campo. Y probablemente también pierda algo de (en el peor de todos) nuestra capacidad de razonar de forma modular.
Campos protegidos
Los campos protegidos restauran parte de nuestra capacidad de razonar de forma modular. Dependiendo del idioma,
protected
puede restringir la capacidad de establecer un campo para todas las subclases o todas las subclases y clases del mismo paquete. A menudo ocurre que no tenemos acceso a todas las subclases cuando razonamos sobre la corrección de un objeto que estamos escribiendo. Por ejemplo, podría estar escribiendo un componente o biblioteca que luego se utilizará en un programa más grande (o en varios programas más grandes), algunos de los cuales pueden no haberse escrito aún. Por lo general, no sabrá si puede subclasificarse y de qué manera.Sin embargo, generalmente corresponde a una subclase mantener el objeto invariable de la clase que extiende. Por lo tanto, en un lenguaje donde protección significa solo "subclase", y donde somos disciplinados para asegurarnos de que las subclases siempre mantengan los invariantes de su superclase, se podría argumentar que la opción de usar protegido en lugar de privado pierde solo una modularidad mínima .
Aunque he estado hablando del razonamiento formal, a menudo se piensa que cuando los programadores razonan informalmente sobre la corrección de su código, a veces también se basan en tipos similares de argumentos.
fuente
private
las variables en una clase son mejores queprotected
por la misma razón que unabreak
declaración dentro de unswitch
bloque es mejor que unagoto label
declaración; que es que los programadores humanos son propensos a errores.protected
Las variables se prestan al abuso no intencional (errores del programador), tal como lagoto
declaración se presta a la creación de código de espagueti.¿Es posible escribir código de trabajo libre de errores usando
protected
variables de clase? ¡Sí, por supuesto! Así como es posible escribir código de trabajo libre de errores usandogoto
; pero como dice el cliché "¡Solo porque puedas, no significa que debas!"Las clases, y de hecho el paradigma OO, existen para protegerse contra los desafortunados programadores humanos propensos a errores que cometen errores. La defensa contra los errores humanos es tan buena como las medidas defensivas integradas en la clase. Hacer la implementación de tu clase
protected
es el equivalente a hacer un enorme agujero en las paredes de una fortaleza.Las clases base no tienen absolutamente ningún conocimiento de las clases derivadas. En lo que respecta a una clase base, en
protected
realidad no le brinda más protección quepublic
, porque no hay nada que impida que una clase derivada cree unpublic
captador / establecedor que se comporte como una puerta trasera.Si una clase base permite el acceso sin obstáculos a los detalles de su implementación interna, entonces se vuelve imposible para la clase misma defenderse de los errores. Las clases base no tienen absolutamente ningún conocimiento de sus clases derivadas y, por lo tanto, no tienen forma de protegerse contra los errores cometidos en esas clases derivadas.
Lo mejor que puede hacer una clase base es ocultar la mayor parte de su implementación posible
private
y establecer suficientes restricciones para evitar que se rompan los cambios de las clases derivadas o cualquier otra cosa fuera de la clase.En definitiva, existen lenguajes de alto nivel para minimizar los errores humanos. También existen buenas prácticas de programación (como los principios SOLID ) para minimizar los errores humanos.
Los desarrolladores de software que ignoran las buenas prácticas de programación tienen muchas más posibilidades de fallar y es más probable que produzcan soluciones rotas que no se pueden mantener. Quienes siguen las buenas prácticas tienen muchas menos posibilidades de fracasar y es más probable que produzcan soluciones de mantenimiento que funcionen.
fuente
public
detalles de implementación porque se presta a un código que no se puede mantener. Un programador competente evitaríagoto
porque se presta a un código que no se puede mantener. Sin embargo, el mundo real es tal que a veces te pierdes en un terrible desastre de código heredado, y no tienes más remedio que usarlogoto
, y por esa misma razón a veces no tienes más remedio que usar detalles de implementación públicos / protegidos.public
propiedades / interfaz, pero no con código que solo usagoto
.goto
o es solo porque has leído artículos como ese? Entiendo por quégoto
es malo porque he pasado 2 años trabajando en código antiguo que lo usa; y he pasado incluso más tiempo trabajando en código donde las "clases" tienen sus detalles de implementación filtrados en todas partespublic
y losfriend
especificadores utilizados como hacks rápidos / fáciles / sucios. No acepto el argumento de que "exponer los detalles de implementación públicamente" no puede causar un desorden de espagueti entrelazado con el mismo nivel de gravedad quegoto
porque sí puede hacerlo.Las clases heredables tienen dos contratos: uno con titulares de referencias de objeto y con clases derivadas. Los miembros públicos están obligados por el contrato con los titulares de referencia y los miembros protegidos están obligados por el contrato con las clases derivadas.
Hacer miembros lo
protected
convierte en una clase base más versátil, pero a menudo limitará las formas en que las versiones futuras de la clase podrían cambiar. Hacer miembrosprivate
permite que el autor de la clase tenga más versatilidad para cambiar el funcionamiento interno de la clase, pero limita los tipos de clases que pueden derivarse útilmente de ella.Como ejemplo,
List<T>
en .NET hace que la tienda de respaldo sea privada; si estuviera protegido, los tipos derivados podrían hacer algunas cosas útiles que de otro modo no serían posibles, pero las versiones futuras deList<T>
para siempre tendrían que usar su anticuada tienda de respaldo monolítico incluso para listas que contienen millones de artículos. Hacer que la tienda de respaldo sea privada permitiría que las futuras versionesList<T>
utilicen una tienda de respaldo más eficiente sin romper las clases derivadas.fuente
Creo que hay una suposición clave en su argumento de que cuando alguien escribe una clase no sabe quién podría extender esa clase en el futuro y por qué razón . Teniendo en cuenta esta suposición, su argumento tendría mucho sentido porque cada variable que haga privada podría cortar alguna vía de desarrollo en el futuro. Sin embargo, rechazaría esa suposición.
Si se rechaza ese supuesto, solo hay dos casos a considerar.
En este caso, el autor sabe que alguien extenderá la clase y por qué y, por lo tanto, sabrá exactamente qué proteger y qué hacer privado. Están utilizando la distinción privada / protegida para comunicar una especie de interfaz al usuario que crea la subclase.
Este caso debería ser raro (se podría argumentar que no es legítimo), y no se prefiere simplemente modificar la clase original en la base de código original. También podría ser un síntoma de mal diseño. En esos casos, preferiría que la persona que piratea el comportamiento solo use otros hacks como amigos (C / C ++) y
setAccessible(true)
(Java).Creo que es seguro rechazar esa suposición.
Esto generalmente se remonta a la idea de composición sobre la herencia . La herencia a menudo se enseña como una forma ideal de reducir la reutilización del código, sin embargo, rara vez debería ser la primera opción para la reutilización del código. No tengo un argumento simple de derribo y puede ser bastante difícil y polémico de entender. Sin embargo, en mi experiencia con el modelado de dominios, he descubierto que rara vez uso la herencia sin tener una comprensión muy clara de quién heredará mi clase y por qué.
fuente
Los tres niveles de acceso tienen su caso de uso, OOP estaría incompleto sin ninguno de ellos. Por lo general lo haces
Y se desvía de este esquema general solo si hay una buena razón ™ . Tenga cuidado con "esto me facilitará la vida cuando pueda acceder libremente desde afuera" (y afuera también incluye subclases). Cuando implemento jerarquías de clases, a menudo empiezo con clases que no tienen miembros protegidos, hasta que llego a subclasificarlas / extenderlas / especializarlas, convirtiéndome en las clases base de un marco / kit de herramientas y, a veces, moviendo parte de su funcionalidad original un nivel más arriba.
fuente
Una pregunta más interesante, quizás, es por qué es necesario cualquier otro tipo de campo que el privado. Cuando una subclase necesita interactuar con los datos de una superclase, hacerlo directamente crea un acoplamiento directo entre los dos, mientras que el uso de métodos para proporcionar la interacción entre los dos permite un nivel de indirección que puede hacer posibles cambios en el superclase que de otro modo sería muy difícil.
Varios idiomas (por ejemplo, Ruby y Smalltalk) no proporcionan campos públicos, por lo que se desalienta a los desarrolladores de permitir el acoplamiento directo a sus implementaciones de clase, pero ¿por qué no ir más allá y solo tienen campos privados? No habría pérdida de generalidad (porque la superclase siempre puede proporcionar accesores protegidos para la subclase), pero garantizaría que las clases siempre tengan al menos un pequeño grado de aislamiento de sus subclases. ¿Por qué este no es un diseño más común?
fuente
Aquí hay muchas buenas respuestas, pero de todos modos arrojaré mis dos centavos. :-)
Privado es bueno por la misma razón que los datos globales son malos.
Si una clase declara datos privados, entonces usted sabe absolutamente que el único código que juega con estos datos es el código de la clase. Cuando hay un error, no tiene que buscar en toda la creación para encontrar cada lugar que pueda cambiar estos datos. Sabes que está en la clase. Cuando realiza un cambio en el código, y cambia algo sobre cómo se usa este campo, no tiene que rastrear todos los lugares potenciales que podrían usar este campo y estudiar si su cambio planificado los romperá. Sabes que los únicos lugares están dentro de la clase.
He tenido muchas, muchas veces que he tenido que hacer cambios en las clases que están en una biblioteca y que usan varias aplicaciones, y tengo que caminar con mucho cuidado para asegurarme de no romper alguna aplicación de la que no sé nada. Cuantos más datos públicos y protegidos haya, mayor será la posibilidad de problemas.
fuente
Creo que vale la pena mencionar algunas opiniones disidentes.
En teoría, es bueno tener un nivel de acceso controlado por todas las razones mencionadas en otras respuestas.
En la práctica, con demasiada frecuencia cuando reviso el código, veo personas (a quienes les gusta usar privado), que cambian el nivel de acceso de privado -> protegido y no muy seguido de protegido -> público. Casi siempre, cambiar las propiedades de la clase implica modificar setters / getters. Perdieron gran parte de mi tiempo (revisión de código) y de ellos (cambio de código).
También me molesta que eso signifique que sus clases no están cerradas por modificación.
Eso fue con el código interno donde siempre puedes cambiarlo si lo necesitas también. La situación es peor con el código de terceros cuando no es tan fácil cambiar el código.
Entonces, ¿cuántos programadores piensan que es una molestia? Bueno, ¿cuántos están usando lenguajes de programación que no tienen privado? Por supuesto, las personas no solo usan esos idiomas porque no tienen especificadores privados, sino que ayuda a simplificar los idiomas y la simplicidad es importante.
Imo es muy similar a la escritura dinámica / estática. En teoría, la escritura estática es muy buena. En la práctica, sólo se impide al igual que el 2% de los errores de la irrazonable eficacia de la dinámica escribiendo ... . El uso de privado probablemente evita errores menores que eso.
Creo que los principios SOLID son buenos, deseo que las personas se preocupen por ellos más de lo que se preocupan por crear una clase con público, protegido y privado.
fuente
También me gustaría agregar otro ejemplo práctico de por qué
protected
no es suficiente. En mi universidad, los primeros años emprenden un proyecto en el que tienen que desarrollar una versión de escritorio de un juego de mesa (que luego se desarrolla una IA y se conecta a otros jugadores a través de una red). Se les proporciona un código parcial para comenzar, incluido un marco de prueba. Algunas de las propiedades de la clase principal del juego están expuestasprotected
para que las clases de prueba que extienden esta clase tengan acceso a ellas. Pero estos campos no son información confidencial.Como un TA para la unidad, a menudo veo a los estudiantes simplemente haciendo todo su código agregado
protected
opublic
(tal vez porque vieron el otroprotected
y otraspublic
cosas y asumieron que deberían hacer lo mismo). Les pregunto por qué su nivel de protección es inapropiado, y muchos no saben por qué. La respuesta es que la información sensible que están exponiendo a subclases significa que otro jugador puede hacer trampa simplemente ampliando esa clase y accediendo a la información altamente sensible para el juego (esencialmente la ubicación oculta de los oponentes, supongo que sería similar a cómo sería si usted podría ver las piezas de sus oponentes en un tablero de acorazados ampliando alguna clase). Eso hace que su código sea muy peligroso en el contexto del juego.Aparte de eso, hay muchas otras razones para mantener algo privado incluso para sus subclases. Podría ser ocultar detalles de implementación que podrían estropear el funcionamiento correcto de la clase si alguien que no sabe necesariamente lo que está haciendo cambia (pensando principalmente en otras personas que usan su código aquí).
fuente
Los métodos / variables privadas generalmente estarán ocultos de una subclase. Eso puede ser algo bueno.
Un método privado puede hacer suposiciones sobre los parámetros y dejar la verificación de la cordura a la persona que llama.
Un método protegido debe verificar las entradas de cordura.
fuente
"privado" significa: No está destinado a ser cambiado o accedido por nadie excepto la clase misma. No está destinado a ser modificado o accedido por subclases Subclases? ¿Qué subclases? ¡No se supone que subclase esto!
"protegido" significa: Sólo destinado a ser cambiado o accedido por clases o subclases. Probable deducción que se supone que debe subclasificar, de lo contrario, ¿por qué "protegido" y no "privado"?
Hay una clara diferencia aquí. Si hago algo privado, se supone que debes mantener tus dedos sucios fuera de él. Incluso si eres una subclase.
fuente
Algunos razonamiento sobre
private
vs.protected
métodos :private
Los métodos evitan la reutilización del código. Una subclase no puede usar el código en el método privado y puede que tenga que implementarlo nuevamente, o volver a implementar los métodos que originalmente dependen del método privado & c.Por otro lado, cualquier método que no
private
se puede ver como una API proporcionada por la clase al "mundo exterior", en el sentido de que las subclases de terceros también se consideran "mundo exterior", como sugirió otra persona en su respuesta ya.¿Es eso algo malo? No lo creo.
Por supuesto, una API (pseudo) pública bloquea el programador original y dificulta la refactorización de esas interfaces. Pero visto al revés, ¿por qué un programador no debería diseñar sus propios "detalles de implementación" de una manera tan limpia y estable como su API pública? ¿Debería usarlo
private
para ser descuidado sobre la estructuración de su código "privado"? ¿Pensando que tal vez podría limpiarlo más tarde, porque nadie se dará cuenta? - No.El programador también debe pensar un poco en su código "privado", para estructurarlo de una manera que permita o incluso promueva la reutilización de la mayor cantidad posible en primer lugar. Entonces, las partes no privadas pueden no convertirse en una carga tan pesada en el futuro como algunos temen.
Veo una gran cantidad de código (marco) que adopta un uso inconsistente de
private
:protected
métodos comunes que no hacen nada más que delegar a un método privado.protected
, métodos no finales cuyo contrato solo se puede cumplir a través del acceso directo a campos privados también.Estos métodos no pueden ser anulados / mejorados lógicamente, aunque técnicamente no hay nada allí para hacer que eso (compilador) sea obvio.
¿Quieres extensibilidad y herencia? No hagas tus métodos
private
.¿No quieres alterar cierto comportamiento de tu clase? Haz tus métodos
final
.¿Realmente no se puede invocar su método fuera de un contexto determinado y bien definido? Haga su método
private
y / o piense cómo puede hacer que el contexto bien definido requerido esté disponible para su reutilización a través de otroprotected
método de contenedor.Es por eso que recomiendo usar con
private
moderación. Y no confundirprivate
confinal
. - Si la implementación de un método es vital para el contrato general de la clase y, por lo tanto, no debe reemplazarse / anularse, ¡hágalofinal
!Para los campos,
private
no es realmente malo. Siempre y cuando el (los) campo (s) se puedan "usar" razonablemente a través de métodos apropiados (¡eso no esgetXX()
osetXX()
!).fuente
private
vsprotected
, da consejos erróneos (¿"usar conprivate
moderación"?) Y va en contra del consenso general sobre el diseño OO. Usarprivate
no tiene nada que ver con esconder código descuidado.private
ayuda a mantener limpia la API.Privado : cuando tiene algo que nunca será útil para que una subclase llame o anule.
Protegido : cuando tienes algo que tiene una implementación / constante específica de subclase.
Un ejemplo:
fuente
Me fue difícil entender este asunto, así que me gustaría compartir una parte de mi experiencia:
$classInstance->field
. Y el truco de que es "esto es todo". Los niños de tu clase tendrán acceso completo a ella, porque es su parte interna legítima.ACTUALIZACIÓN: un ejemplo práctico de una tarea real que he resuelto. Aquí está: tiene un token, como USB o LPT (ese fue mi caso), y tiene un middleware. El token le pide un código PIN, se abre si es correcto y puede enviar una parte encriptada y varias claves para descifrar. Las claves se almacenan en token, no puede leerlas, solo usarlas. Y había claves temporales para una sesión, firmadas por una clave en un token, pero almacenadas en un middleware. Se suponía que la clave temporal no debía filtrarse en todas partes, solo para existir en el nivel del conductor. Y utilicé campos privados para almacenar esta clave temporal y algunos datos relacionados con la conexión de hardware. Así que ningún derivado pudo usar no solo una interfaz pública, sino también algunos protegidosSubrutinas "prácticas" que hice para una tarea, pero no pude abrir una caja fuerte con las teclas y la interacción HW. ¿Tiene sentido?
fuente