Pregunta

Rara vez uso la herencia, pero cuando lo hago, nunca uso atributos protegidos porque creo que rompe la encapsulación de las clases heredadas.

¿Utiliza atributos protegidos?Para qué los utiliza ?

¿Fue útil?

Solución

En esto entrevista sobre diseño de Bill Venners, Joshua Bloch, autor de Java efectivo dice:

Subclases de confianza

Bill Venners: ¿Debo confiar en las subclases más íntimamente que las no subclases?Por ejemplo, ¿hago que sea más fácil para una implementación de subclase romperme de lo que lo haría para una nocronización?En particular, ¿cómo se siente acerca de los datos protegidos?

Josh Bloch: Escribir algo que sea subclasable y robusto contra una subclase maliciosa es en realidad algo bastante difícil de hacer, suponiendo que le dé acceso a la subclase a sus estructuras de datos internos.Si la subclase no tiene acceso a nada que un usuario ordinario no haga, entonces es más difícil para la subclase hacer daño.Pero a menos que haga todos sus métodos finales, la subclase aún puede romper sus contratos simplemente haciendo las cosas incorrectas en respuesta a la invocación del método.Esa es precisamente la razón por la cual las clases críticas de seguridad como la cadena son finales.De lo contrario, alguien podría escribir una subclase que haga que las cuerdas parezcan mutables, lo que sería suficiente para romper la seguridad.Entonces debes confiar en tus subclases.Si no confía en ellos, entonces no puede permitirlos, porque las subclases pueden hacer que una clase viole sus contratos.

En cuanto a los datos protegidos en general, es un mal necesario.Debe mantenerse al mínimo.La mayoría de los datos protegidos y los métodos protegidos equivalen a comprometerse con un detalle de implementación.Un campo protegido es un detalle de implementación que está haciendo visible para las subclases.Incluso un método protegido es una pieza de estructura interna que está haciendo visible para las subclases.

La razón por la que lo hace visible es que a menudo es necesario para permitir que las subclases hagan su trabajo, o que lo haga de manera eficiente.Pero una vez que lo has hecho, estás comprometido con eso.Ahora es algo que no puede cambiar, incluso si luego encuentra una implementación más eficiente que ya no implica el uso de un campo o método en particular.

Entonces, todas las demás cosas son iguales, no deberías tener ningún miembro protegido en absoluto.Pero dicho esto, si tiene muy pocos, entonces su clase puede no ser utilizable como una súper clase, o al menos no como una súper clase eficiente.A menudo lo descubres después del hecho.Mi filosofía es tener la menor cantidad de miembros protegidos como sea posible cuando escriba la clase por primera vez.Luego intenta subclasificarlo.Puede descubrir que sin un método protegido particular, todas las subclases tendrán que hacer algo malo.

Como ejemplo, si nos fijamos en AbstractList, encontrará que hay un método protegido para eliminar un rango de la lista de una sola vez (removeRange).¿Por qué está eso ahí?Porque el idioma normal para eliminar un rango, según la API pública, es llamar subList para conseguir un sub-Listy luego llamar clear en ese sub-List.Sin este método protegido en particular, sin embargo, lo único que clear podría hacer es eliminar repetidamente elementos individuales.

Piénsalo.Si tiene una representación de matriz, ¿qué hará?Se colapsará repetidamente la matriz, haciendo el orden y el trabajo n veces.Por lo tanto, tomará una cantidad cuadrática de trabajo, en lugar de la cantidad lineal de trabajo que debería.Al proporcionar este método protegido, permitimos cualquier implementación que pueda eliminar eficientemente un rango completo para hacerlo.Y cualquier razonable List La implementación puede eliminar un rango de manera más eficiente a la vez.

Que necesitaríamos este método protegido es algo que tendría que ser mucho más inteligente que yo para conocer por adelantado.Básicamente, implementé la cosa.Luego, a medida que comenzamos a subclase, nos dimos cuenta de que la eliminación de rango era cuadrática.No podíamos pagar eso, así que puse el método protegido.Creo que ese es el mejor enfoque con métodos protegidos.Ponga lo menos posible y luego agregue más según sea necesario.Los métodos protegidos representan compromisos con diseños que puede cambiar.Siempre puede agregar métodos protegidos, pero no puede sacarlos.

Bill Venners: ¿Y los datos protegidos?

Josh Bloch: Lo mismo, pero aún más.Los datos protegidos son aún más peligrosos en términos de estropear sus invariantes de datos.Si le da a otra persona acceso a algunos datos internos, tiene un reinado gratuito sobre él.

Version corta:rompe la encapsulación pero es un mal necesario que debe mantenerse al mínimo.

Otros consejos

C#:

Utilizo protected para métodos abstractos o virtuales que quiero que las clases base anulen.También hago un método protegido si las clases base pueden llamarlo, pero no quiero que se llame fuera de la jerarquía de clases.

Es posible que los necesite para el atributo estático (o 'global') del que desea que se beneficien sus subclases o clases del mismo paquete (si se trata de Java).

Esos atributos finales estáticos que representan algún tipo de "valor constante" rara vez tienen una función captadora, por lo que un atributo final estático protegido podría tener sentido en ese caso.

Scott Meyers dice no utilice atributos protegidos en Effective C++ (3.ª ed.):

Artículo 22:Declarar privados a los miembros de datos.

La razón es la misma que tú das:rompe encapsulaciones.La consecuencia es que, de lo contrario, los cambios locales en el diseño de la clase podrían romper los tipos dependientes y dar lugar a cambios en muchos otros lugares.

Nunca hay buenas razones para tener atributos protegidos.Una clase base debe poder depender del estado, lo que significa restringir el acceso a los datos a través de métodos de acceso.No puedes darle a nadie acceso a tus datos privados, ni siquiera a los niños.

El protected La palabra clave es un error conceptual y una chapuza en el diseño del lenguaje, y varios lenguajes modernos, como Nim y Ceilán (ver http://ceylon-lang.org/documentation/faq/language-design/#no_protected_modifier), que han sido diseñados cuidadosamente en lugar de simplemente copiar errores comunes, no tienen esa palabra clave.

No son los miembros protegidos los que rompen la encapsulación, sino la exposición de los miembros que no deberían estar expuestos lo que rompe la encapsulación...no importa si son protegidos o públicos.El problema con protected es que es desacertado y engañoso...miembros declarantes protected (en vez de private) no los protege, hace todo lo contrario, exactamente como public hace.Un miembro protegido, al ser accesible fuera de la clase, está expuesto al mundo y, por lo tanto, su semántica debe mantenerse para siempre, tal como es el caso de public.Toda la idea de "protegido" es una tontería...la encapsulación no es seguridad y la palabra clave simplemente aumenta la confusión entre las dos.Puedes ayudar un poco evitando todos los usos de protected en sus propias clases: si algo es una parte interna de la implementación, no es parte de la semántica de la clase y puede cambiar en el futuro, hágalo privado o interno a su paquete, módulo, ensamblaje, etc.Si es una parte inmutable de la semántica de la clase, entonces hágala pública y así no molestará a los usuarios de su clase que pueden ver que hay un miembro útil en la documentación pero no pueden usarlo, a menos que estén creando su propia clase. propias instancias y puede acceder a ellas mediante subclases.

No uso atributos protegidos en Java porque allí solo están protegidos por paquetes.Pero en C++, los usaré en clases abstractas, permitiendo que la clase heredera las herede directamente.

En general, no, realmente no desea utilizar miembros con datos protegidos.Esto es doblemente cierto si estás escribiendo una API.Una vez que alguien hereda de tu clase, nunca podrás realmente hacer mantenimiento y no romperlos de alguna manera de una manera extraña y, a veces, salvaje.

Creo que los atributos protegidos son una mala idea.yo suelo Estilo de verificación para hacer cumplir esa regla con mis equipos de desarrollo de Java.

Recientemente trabajé en un proyecto en el que el miembro "protegido" era una muy buena idea.La jerarquía de clases era algo como:

[+] Base
 |
 +--[+] BaseMap
 |   |
 |   +--[+] Map
 |   |
 |   +--[+] HashMap
 |
 +--[+] // something else ?

La Base implementó un std::list pero nada más.El acceso directo a la lista estaba prohibido al usuario, pero como la clase Base estaba incompleta, dependía de todos modos de clases derivadas para implementar la dirección indirecta a la lista.

La dirección indirecta podría venir de al menos dos sabores:std::map y stdext::hash_map.Ambos mapas se comportarán de la misma manera, pero el hash_map necesita que la clave sea hash (en VC2003, convertible a size_t).

Entonces BaseMap implementó un TMap como un tipo de plantilla que era un contenedor similar a un mapa.

Map y HashMap eran dos clases derivadas de BaseMap, una especializada en BaseMap en std::map y la otra en stdext::hash_map.

Entonces:

  • La base no se podía utilizar como tal (¡no había accesos públicos!) y solo proporcionaba características y código comunes

  • BaseMap necesitaba lectura/escritura fácil en un std::list

  • Map y HashMap necesitaban un fácil acceso de lectura/escritura al TMap definido en BaseMap.

Para mí, la única solución fue usar protected para std::list y las variables miembro de TMap.No había manera de que los pusiera como "privados" porque de todos modos expondría todas o casi todas sus características a través de accesos de lectura/escritura.

Al final, supongo que si terminas dividiendo tu clase en múltiples objetos, cada derivación agrega características necesarias a su clase madre, y solo la clase más derivada es realmente utilizable, entonces proteger es el camino a seguir.El hecho de que el "miembro protegido" fuera una clase y, por lo tanto, fuera casi imposible de "romper", ayudó.

Pero por lo demás, se debe evitar lo protegido tanto como sea posible (es decir:Utilice privado de forma predeterminada y público cuando deba exponer el método).

Yo los uso.En resumen, es una buena manera si desea compartir algunos atributos.Por supuesto, podrías escribir funciones set/get para ellos, pero si no hay validación, ¿cuál es el punto?También es más rápido.

Considera esto:tienes una clase que es tu clase base.Tiene bastantes atributos que no desea utilizar en los objetos secundarios.Puede escribir una función get/set para cada uno, o simplemente configurarlas.

Mi ejemplo típico es un controlador de archivos/transmisiones.Quiere acceder al controlador (es decir,descriptor de archivo), pero desea ocultarlo de otras clases.Es mucho más fácil que escribir una función set/get para ello.

En general, sí.Un método protegido suele ser mejor.

En uso, hay un nivel de simplicidad dado por el uso de un protegido final Variable para un objeto que es compartida por todos los hijos de una clase.Siempre desaconsejaría usarlo con primitivas o colecciones, ya que los contratos son imposibles de definir para esos tipos.

Últimamente he llegado a separar las cosas que haces con primitivas y colecciones sin procesar de las que haces con clases bien formadas.Las primitivas y las colecciones SIEMPRE deben ser privadas.

Además, ocasionalmente comencé a exponer variables miembro públicas cuando se declaran finales y son clases bien formadas que no son demasiado flexibles (nuevamente, no primitivas ni colecciones).

Este no es un atajo estúpido, lo pensé muy seriamente y decidí que no hay absolutamente ninguna diferencia entre un público final variable que expone un objeto y un captador.

Depende de lo que quieras.Si desea una clase rápida, los datos deben protegerse y utilizar métodos públicos y protegidos.Porque creo que debes asumir que los usuarios que derivan de tu clase conocen bastante bien tu clase o al menos han leído tu manual en la función que van a anular.

Si tus usuarios se meten con tu clase no es tu problema.Cada usuario malintencionado puede agregar las siguientes líneas al anular uno de sus virtuales:

(C#)

static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...

No puedes sellar todas las clases para evitar esto.

Por supuesto, si desea una restricción en algunos de sus campos, utilice funciones o propiedades de acceso o algo que desee y haga que ese campo sea privado porque no hay otra solución...

Pero a mí personalmente no me gusta apegarme a los principios de oop a toda costa.Especialmente crear propiedades con el único propósito de hacer que los datos de los miembros sean privados.

(C#):

private _foo;
public foo
{
   get {return _foo;}
   set {_foo=value;}
}

Esta fue mi opinión personal.

Pero haga lo que su jefe requiera (si quiere campos privados, hágalo).

Utilizo variables/atributos protegidos dentro de clases base que sé que no planeo convertir en métodos.De esa manera, las subclases tienen acceso completo a sus variables heredadas y no tienen la sobrecarga (creada artificialmente) de pasar por captadores/definidores para acceder a ellas.Un ejemplo es una clase que utiliza un flujo de E/S subyacente;hay pocas razones para no permitir que las subclases accedan directamente al flujo subyacente.

Esto está bien para las variables miembro que se utilizan de forma directa y sencilla dentro de la clase base y todas las subclases.Pero para una variable que tiene un uso más complicado (por ejemplo, acceder a ella causa efectos secundarios en otros miembros dentro de la clase), una variable directamente accesible no es apropiada.En este caso, se puede hacer privado y en su lugar se pueden proporcionar captadores/definidores públicos/protegidos.Un ejemplo es un mecanismo de almacenamiento en búfer interno proporcionado por la clase base, donde acceder a los búferes directamente desde una subclase comprometería la integridad de los algoritmos utilizados por la clase base para administrarlos.

Es una decisión de juicio de diseño, basada en qué tan simple es la variable miembro y cómo se espera que lo sea en versiones futuras.

La encapsulación es excelente, pero puede llevarse demasiado lejos.He visto clases cuyos propios métodos privados accedían a sus variables miembro usando solo métodos getter/setter.Esto es excesivo, ya que si una clase no puede confiar en sus propios métodos privados con sus propios datos privados, ¿en quién puede confiar?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top