¿Por qué tener campos privados, no está lo suficientemente protegido?

265

¿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 privatemétodos protected. Sin embargo, ocultar detalles del mundo exterior es importante, por lo que necesitamos protectedy no solo public.

Mi pregunta es: ¿Conoces un caso de uso importante en el que, en privatelugar de protectedser una buena herramienta, o dos opciones " protected& public" serían suficientes para los idiomas OOP?

Adam Libuša
fuente
236
A los votantes negativos: si bien también estoy en total desacuerdo con las premisas del OP, estoy votando esta pregunta porque es perfectamente coherente y vale la pena responder. Sí, el OP necesita que se le diga por qué esto está mal, pero la forma de hacerlo es escribir una respuesta (o sugerir modificaciones a las respuestas existentes), no votar negativamente solo porque aún no lo ha descubierto por sí mismo.
Ixrec
18
Las clases derivadas son parte del mundo exterior.
CodesInChaos
20
No olvide que protegido no siempre significa que el acceso está bloqueado en la jerarquía de herencia. En Java, también otorga acceso a nivel de paquete.
berry120
8
Mi profesor solía decir que "hay cosas que no les diría a mis hijos. Esos son mis campos privados".
SáT

Respuestas:

225

Porque como dices, protectedaú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 protectedmiembros son esencialmente una "API pública para subclases" de una clase y deben permanecer estables y compatibles con versiones anteriores tanto como los publicmiembros. Si no tuviéramos la capacidad de crear privatemiembros 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.

Ixrec
fuente
11
Te podría arrojar más de +1 si pudiera, ya que esta es la primera vez privateque 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 usaba privateanteriormente, siempre sentí una buena documentación y una convención de nomenclatura como _foopara 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 y privateexclusiva.
abluejelly
Originalmente ignoré el caso del código de bibliotecas y marcos públicos y pensé más o menos solo en términos de "código de cliente". La optimización de la implementación interna es un buen ejemplo para mi pregunta, aunque me pregunto si esto realmente sucede en realidad (especialmente cuando muchas personas recomiendan que una clase no debe tener más de 1000 líneas de código). En general, me gusta el enfoque de Ruby, donde privado es una especie de recomendación: "Aquí hay dragones, proceda con cuidado".
Adam Libuša
99
@ AdamLibuša Si bien este es un negocio mucho más grande si su código es público, aún se aplica incluso si usted es el autor de todos los clientes de la clase. El problema simplemente cambia de ciertos refactores que son imposibles a ciertos refactores que son tediosos y propensos a errores. La optimización es en realidad la razón menos común para estos refactores en mi experiencia (aunque principalmente hago Javascript), por lo general es más como un error que expuso un defecto de implementación fundamental que requiere reestructurar el gráfico de dependencia / llamada de los diversos bits internos para lograr Una solución realmente robusta.
Ixrec
55
"" tarde o temprano, va a hacer una subclase de cada clase ", casi con toda seguridad no es el caso". Y más al punto, es casi seguro que no, y ciertamente NO DEBERÍAS, anular todas las funciones de una clase y cambiar el uso de cada elemento de datos en una clase. Algunas cosas deberían escribirse lógicamente en piedra para que la clase tenga algún significado.
Jay
1
Te arrojaría más de +1 si pudiera => Eso es lo que llamamos recompensa @abluejelly
Thomas Ayoub
256

En OOP, tarde o temprano, vas a hacer una subclase de una clase

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 ++) o sealed(C #).

Es bueno comprender y poder modificar la implementación por completo.

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.

Sebastian Redl
fuente
34
+1 para el escenario teórico de "afectado por una cantidad infinita de código"
Broken_Window
13
Quizás valga la pena señalar también que los diseñadores de C # en realidad decidieron prohibir anular un método heredado a menos que esté explícitamente marcado como virtual, por las razones descritas en esta respuesta.
Will Vousden
11
O simplemente: protectedes para métodos, no para miembros de datos.
Matthieu M.
77
No puedo encontrarlo ahora, pero recuerdo haber leído una respuesta bien escrita de @EricLippert acerca de por qué MS sealedpartes 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.
Dan Neely
44
@ ThorbjørnRavnAndersen "mientras se desarrolla pero no cuando se libera": ¿cómo se supone que debe saber una clase? ¿Realmente desea compilar una versión de prueba que sea diferente de la versión lanzada cuando ni siquiera puede probar la versión lanzada? Las pruebas no deberían necesitar acceder a cosas privadas de todos modos.
Sebastian Redl
33

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.

Robbie Dee
fuente
2
+1 para "montar un entrenador y caballos a través de" cualquier cosa. Es una gran frase que desearía haber escuchado más.
Nic Hartley
2
Si alguien necesita subclasificar su clase, ¿tal vez debería tener acceso a sus salvaguardas? Y si no desea que alguien cambie sus salvaguardas para lograr algún objetivo, ¿tal vez no comparta el código? Funciona así en Ruby: privado es más o menos una recomendación.
Adam Libuša
3
@ AdamLibuša "¿No comparte el código"? Tan pronto como publicas cualquier DLL, estás compartiendo el código: todas las estructuras y métodos y todo está ahí para que todo el mundo lo vea, especialmente con lenguajes que admiten la reflexión de forma predeterminada. Todos pueden hacer lo que quieran con "su código", privatees 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).
Luaan
2
@ AdamLibuša Creo que su confusión proviene principalmente de los diferentes enfoques de OOP que tomaron los diferentes idiomas. La raíz de OOP (como se definió originalmente) es la mensajería , lo que significa que todo es privado, excepto los mensajes a los que responde. En la mayoría de los idiomas OOPish, esto se expone como "mantener sus datos privados", una forma de hacer que la interfaz pública sea lo más pequeña posible. La única forma en que el usuario (ya sea una subclase u otra clase) tiene que manipular su clase es a través de la interfaz pública que definió, de forma similar a cómo usa habitualmente el volante y los pedales para conducir su automóvil :)
Luaan
3
@Luaan +1 para cuando está bien tocar las partes privadas de otros.
Nigel Touch
12

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

  1. Los métodos se han asociado con ellos antes y después de las condiciones (contratos).
  2. Los objetos se han asociado con ellos invariantes.

El razonamiento modular sobre un objeto procede de la siguiente manera (al menos una aproximación al menos)

  1. Probar que el constructor del objeto establece la invariante.
  2. Para cada método no privado, suponga que el objeto invariante y la condición previa del método se mantienen en la entrada, luego pruebe que el cuerpo del código implica que la condición posterior y la condición invariante se mantienen en la salida

Imagine que verificamos y utilizamos object Ael enfoque anterior. Y ahora desea verificar method gde object Bque se pide method fde object A. El razonamiento modular nos permite razonar method gsin tener que reconsiderar la implementación de method f. Siempre que podamos establecer la invariante object Ay la precondición method fen el sitio de la llamada, method g,podemos tomar la condición posterior method fcomo 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 de Atodaví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, protectedpuede 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.

flamingpenguin
fuente
8

privatelas variables en una clase son mejores que protectedpor la misma razón que una breakdeclaración dentro de un switchbloque es mejor que una goto labeldeclaración; que es que los programadores humanos son propensos a errores.

protectedLas variables se prestan al abuso no intencional (errores del programador), tal como la gotodeclaración se presta a la creación de código de espagueti.

¿Es posible escribir código de trabajo libre de errores usando protectedvariables de clase? ¡Sí, por supuesto! Así como es posible escribir código de trabajo libre de errores usando goto; 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 protectedes 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 protectedrealidad no le brinda más protección que public, porque no hay nada que impida que una clase derivada cree un publiccaptador / 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 privatey 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.

Ben Cottrell
fuente
Para downvoter: ¿qué se podría cambiar / mejorar sobre esta respuesta?
Ben Cottrell
No rechacé la votación, pero ¿comparé el nivel de acceso con break / goto?
imel96
@ imel96 No, comparando la razón por la cual se evitan y se desalientan por la "Mejor práctica" (particularmente para escribir código nuevo ). es decir, un programador competente evitaría los publicdetalles de implementación porque se presta a un código que no se puede mantener. Un programador competente evitaría gotoporque 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 usarlo goto, y por esa misma razón a veces no tienes más remedio que usar detalles de implementación públicos / protegidos.
Ben Cottrell
La gravedad es diferente en magnitud, es incomparable. Podría vivir con código que solo tiene publicpropiedades / interfaz, pero no con código que solo usa goto.
imel96
2
@ imel96 ¿Alguna vez has trabajado en código que usa gotoo 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 partes publicy los friendespecificadores 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 que gotoporque sí puede hacerlo.
Ben Cottrell
4

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 protectedconvierte 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 miembros privatepermite 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 de List<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 versiones List<T>utilicen una tienda de respaldo más eficiente sin romper las clases derivadas.

Super gato
fuente
4

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.

  1. El autor de la clase original tenía ideas muy claras de por qué podría extenderse (por ejemplo, es un BaseFoo y habrá varias implementaciones concretas de Foo en el futuro).

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.

  1. El autor de la clase secundaria está intentando piratear algún comportamiento en una clase primaria.

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é.

Paso
fuente
2

Los tres niveles de acceso tienen su caso de uso, OOP estaría incompleto sin ninguno de ellos. Por lo general lo haces

  • Hacer que todas las variables / datos sean privados . No quieres que alguien de afuera se meta con tus datos internos. También métodos que proporcionan funcionalidad auxiliar (piense en cálculos basados ​​en varias variables miembro) a su interfaz pública o protegida : esto es solo para uso interno, y es posible que desee cambiarlo / mejorarlo en el futuro.
  • haz pública la interfaz general de tu clase . Así es como se supone que deben trabajar los usuarios de su clase original, y cómo cree que también deberían verse las clases derivadas. Con el fin de proporcionar una encapsulación adecuada, estos son generalmente solo métodos (y clases / estructuras auxiliares, enumeraciones, typedefs, lo que el usuario necesita para trabajar con sus métodos), no variables.
  • declare los métodos protegidos que podrían ser útiles para alguien que quiera ampliar / especializar la funcionalidad de su clase, pero que no debería formar parte de la interfaz pública ; de hecho, generalmente se aumenta la protección de los miembros privados cuando es necesario. En caso de duda no lo hace, hasta que sepa que
    1. su clase puede / puede / será subclasificada,
    2. y tener una idea clara de cuáles pueden ser los casos de uso de subclases.

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.

Murphy
fuente
1

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?

Jules
fuente
1

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.

Arrendajo
fuente
1

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.

imel96
fuente
1
Si necesita cambiar el código de un tercero cuando lo usa, o no está bien diseñado, o no lo está reutilizando bien. Siempre puede reutilizar una clase no abstracta encapsulándola, pero en la práctica casi nunca necesita subclasificarla.
Little Santi
0

También me gustaría agregar otro ejemplo práctico de por qué protectedno 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 expuestas protectedpara 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 protectedo public(tal vez porque vieron el otro protectedy otras publiccosas 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í).

J_mie6
fuente
1
No entiendo. A menos que los jugadores amplíen dinámicamente las clases mientras el juego se está ejecutando, ¿cómo podría usarse esto para hacer trampa? Esto implicaría que está importando código no confiable a su base de código. Si está diciendo que está dando clases compiladas a los estudiantes y evitando que hagan trampa en su tarea modificando los detalles de implementación, establecer un nivel de protección no hará que la tarea sea más segura. Los estudiantes pueden descompilar bibliotecas o usar la reflexión para su ventaja. Esto, por supuesto, depende del nivel de cumplimiento que busca.
Sam
@sam Generalmente, el código debe escribirse asumiendo que quien lo modifique a continuación no necesitará conocer toda la base de código al revés. Ese incluso podría ser el autor original a veces (paso del tiempo, falta de sueño ...). Hacer trampa podría ser que a los estudiantes se les dé un código esqueleto para desarrollar y cambiar la lógica que no deberían tocar.
Phil Lello
0

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.

Phil Lello
fuente
-1

"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.

gnasher729
fuente
-1

Una de las primeras cosas que hago cuando subclasifico una clase es cambiar un montón de métodos privados a protegidos

Algunos razonamiento sobre privatevs. protected métodos :

privateLos 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 privatepara 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: protectedmé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 privatey / o piense cómo puede hacer que el contexto bien definido requerido esté disponible para su reutilización a través de otro protectedmétodo de contenedor.

Es por eso que recomiendo usar con privatemoderación. Y no confundir privatecon final. - 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ágalo final!

Para los campos, privateno es realmente malo. Siempre y cuando el (los) campo (s) se puedan "usar" razonablemente a través de métodos apropiados (¡eso no es getXX() o setXX()!).

JimmyB
fuente
2
-1 Esto no aborda la pregunta original privatevs protected, da consejos erróneos (¿"usar con privatemoderación"?) Y va en contra del consenso general sobre el diseño OO. Usar privateno tiene nada que ver con esconder código descuidado.
Andres F.
3
Usted menciona que una clase tiene una API, pero no parece hacer la conexión que el usuario de la API no quiere cargar con detalles que no necesitan saber. La situación ideal para el usuario de una clase es que la interfaz contenga solo los métodos que necesita y nada más. Hacer métodos privateayuda a mantener limpia la API.
Ben Cottrell
1
@HannoBinder Estoy de acuerdo en que hay muchas clases que sufren rigidez, sin embargo, la flexibilidad y la reutilización del código son mucho menos importantes que la encapsulación y la mantenibilidad a largo plazo. Considere su caso donde todas las clases tienen miembros protegidos y las clases derivadas pueden meterse con los detalles de implementación que deseen; ¿cómo prueba de manera confiable esas clases sabiendo que cualquier cosa en cualquier parte del código aún no escrito podría causar que ese código falle en cualquier momento? ¿Cuántos "trucos" podría tomar el código antes de que degenere en una gran bola de lodo?
Ben Cottrell
2
Oh, dijiste "miembros". Lo que digo se refiere solo a los métodos . Las variables miembro (campos) son una historia diferente, donde la privacidad está mucho más justificada.
JimmyB
2
La nomenclatura en la discusión está por todas partes porque no es específica de un idioma en particular. "Miembro" en C ++ puede ser datos, función, tipo o plantilla.
JDługosz
-1

¿Conoces un caso de uso importante en el que privado en lugar de protegido sea una buena herramienta, o dos opciones "protegido y público" serían suficientes para los idiomas OOP?

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:

public abstract Class MercedesBenz() extends Car {
  //Might be useful for subclasses to know about their customers
  protected Customer customer; 

  /* Each specific model has its own horn. 
     Therefore: protected, so that each subclass might implement it as they wish
  */
  protected abstract void honk();

  /* Taken from the car class. */
  @Override
  public void getTechSupport(){
     showMercedesBenzHQContactDetails(customer);
     automaticallyNotifyLocalDealer(customer);
  }

  /* 
     This isn't specific for any subclass.
     It is also not useful to call this from inside a subclass,
     because local dealers only want to be notified when a 
     customer wants tech support. 
   */
  private void automaticallyNotifyLocalDealer(){
    ...
  }
}
Arnab Datta
fuente
2
cuando tiene algo que nunca será útil para que una subclase llame o anule , y esto es algo que, en mi opinión, nunca sabe de antemano.
Adam Libuša
Bueno, también podría necesitar presionar el botón de reinicio en mi CMOS. Pero la suposición predeterminada es que realmente no lo necesitaré y, por lo tanto, se coloca dentro del chasis. Lo mismo vale para los métodos. Lo protege si la subclase necesita absolutamente reimplementarlo / llamarlo (ver: tocar la bocina). Lo hace público si otras partes necesitan llamarlo.
Arnab Datta
-2

Me fue difícil entender este asunto, así que me gustaría compartir una parte de mi experiencia:

  • ¿Qué es el campo protegido ? Es nada más que un campo, que no se puede acceder fuera de una clase, es decir, públicamente como esto: $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.
  • ¿Qué es el campo privado ? Es un " verdadero privado " para su propia clase y su propia implementación de esta clase. "Mantener fuera del alcance de los niños", como en el frasco de un medicamento. Tendrá la garantía de que no se puede modificar por los derivados de su clase, sus métodos, cuando se los llame, tendrán exactamente lo que ha declarado

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?

Alexey Vesnin
fuente
¡lo siento chicos! simplemente los estropeé - actualizando mi respuesta =) ¡GRACIAS! Estaba apurado y mal escrito =)
Alexey Vesnin
2
Creo que el OP es consciente de las diferencias entre los dos niveles de protección, pero está interesado en por qué uno debe usarse sobre el otro.
Sam
@ Sam, por favor tome una lectura más profunda de mi respuesta. ¡Son tipos de campo completamente diferentes ! lo único que tienen en común es que no se puede hacer referencia a ambos públicamente
Alexey Vesnin
2
No estoy seguro de a qué te refieres. Soy consciente de las diferencias entre privado y protegido. ¿Quizás entendiste mal lo que quise decir con niveles de protección? Me refiero a los 'niveles de acceso'. Intenté señalar que si bien su respuesta no es mala, no aborda directamente la pregunta. El OP parece saber lo que significa tanto protegido / privado / público, pero no está seguro bajo qué circunstancias desearían elegir uno sobre el otro. Esta puede ser la forma en que ha sido rechazado (no por mí).
Sam
@Sam Creo que he entendido bien: agregué un caso práctico de mi práctica / experiencia real. ¿Es lo que falta en tu punto?
Alexey Vesnin