oop - privada - publico privado protegido java
¿Deberían prohibirse los atributos protegidos? (14)
Creo que los atributos protegidos son una mala idea. Uso CheckStyle para hacer cumplir esa regla con mis equipos de desarrollo de Java.
Rara vez uso la herencia, pero cuando lo hago, nunca uso atributos protegidos porque creo que rompe la encapsulación de las clases heredadas.
¿Usas atributos protegidos? Para qué los utiliza ?
DO#:
Uso protegidos para métodos abstractos o virtuales que deseo que las clases base anulen. También hago que un método esté protegido si las clases base lo llaman, pero no quiero que se invoque fuera de la jerarquía de clases.
En general, no, realmente no desea usar miembros de datos protegidos. Esto es doblemente cierto si estás escribiendo una API. Una vez que alguien hereda de tu clase, nunca podrás realizar tareas de mantenimiento y no romperlas de una manera extraña y, a veces, salvaje.
En general, sí. Un método protegido es usualmente mejor.
En uso, hay un nivel de simplicidad dado al usar una variable final protegida para un objeto que es compartido por todos los niños de una clase. Siempre aconsejo no usarlo con primitivas o colecciones ya que los contratos son imposibles de definir para esos tipos.
Últimamente he separado las cosas que haces con primitivos y colecciones crudas de cosas que haces con clases bien formadas. Primitivas y colecciones SIEMPRE deben ser privadas.
Además, comencé ocasionalmente a exponer las variables de miembro público cuando se declararon definitivas y son clases bien formadas que no son demasiado flexibles (una vez más, no primitivas ni colecciones).
Este no es un atajo estúpido, lo pensé bastante seriamente y decidí que no había absolutamente ninguna diferencia entre una variable pública final que expone un objeto y un getter.
No uso atributos protegidos en Java porque solo están protegidos por paquetes allí. Pero en C ++, los usaré en clases abstractas, permitiendo que la clase heredera los herede directamente.
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 puede dar acceso a nadie a sus datos privados, incluso a los niños.
Puede necesitarlos 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 getter, por lo que un atributo final estático protegido podría tener sentido en ese caso.
Scott Meyers dice que no use atributos protegidos en Effective C ++ (3rd ed.):
Ítem 22: declarar miembros de datos privados.
La razón es la misma que 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 provocar cambios en muchos otros lugares.
Yo los uso En resumen, es una buena forma, si desea compartir algunos atributos. De acuerdo, podrías escribir las 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 desea utilizar en los objetos secundarios. Puede escribir una función get / set para cada uno, o puede simplemente configurarlos.
Mi ejemplo típico es un manejador de archivo / flujo. Desea 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.
Recientemente trabajé en un proyecto donde el miembro "protegido" fue una muy buena idea. La jerarquía de clases era algo así como:
[+] Base
|
+--[+] BaseMap
| |
| +--[+] Map
| |
| +--[+] HashMap
|
+--[+] // something else ?
The Base implementó una std :: list pero nada más. El acceso directo a la lista estaba prohibido para el usuario, pero como la clase Base estaba incompleta, dependía de todas maneras de las clases derivadas para implementar la indirección en la lista.
La indirección podría provenir de al menos dos sabores: std :: map y stdext :: hash_map. Ambos mapas se comportarán de la misma manera, pero por el hecho de que hash_map necesita que la clave sea hashable (en VC2003, convertible a size_t).
Así que BaseMap implementó un TMap como un tipo de plantilla que era un contenedor tipo mapa.
Map y HashMap fueron dos clases derivadas de BaseMap, una BaseMap especializada en std :: map y la otra en stdext :: hash_map.
Asi que:
La base no se podía usar como tal (¡no tenía acceso público!) Y solo proporcionaba funciones comunes y código
BaseMap necesitaba fácil lectura / escritura en 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 de miembro TMap. No había forma de que pusiera esos "privados" porque de todos modos expondría todas o casi todas sus características a través de accesadores de lectura / escritura.
Al final, supongo que si divides tu clase en múltiples objetos, cada derivación agrega funciones necesarias a su clase madre, y solo la clase más derivada es realmente útil, entonces protegido es el camino a seguir. El hecho de que el "miembro protegido" era una clase, y por lo tanto, era casi imposible "romper", ayudó.
De lo contrario, debe evitarse tanto como sea posible (es decir: usar privado de forma predeterminada y público cuando debe exponer el método).
En esta entrevista sobre Diseño de Bill Venners, Joshua Bloch, el autor de Effective Java dice:
Subclases de confianza
Bill Venners: ¿Debería 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 una subclase romperme que para una subclase? En particular, ¿cómo te sientes con los datos protegidos?
Josh Bloch: escribir algo que sea tanto subclassable como robusto frente a una subclase maliciosa es en realidad algo bastante difícil de hacer, suponiendo que le dé acceso a la subclase a sus estructuras internas de datos. Si la subclase no tiene acceso a nada que un usuario común no tenga, entonces es más difícil que la subclase haga daño. Pero a menos que finalice todos sus métodos, la subclase aún puede romper sus contratos simplemente haciendo las cosas incorrectas en respuesta a la invocación del método. Es precisamente por eso que las clases críticas de seguridad como String son definitivas. De lo contrario, alguien podría escribir una subclase que haga que las cadenas parezcan mutables, lo que sería suficiente para romper la seguridad. Entonces debes confiar en tus subclases. Si no confías en ellos, entonces no puedes permitirlos, porque las subclases pueden hacer que una clase viole tan fácilmente 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 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 para hacerlo de manera eficiente. Pero una vez que lo has hecho, estás comprometido con él. Ahora es algo que no puede cambiar, incluso si luego encuentra una implementación más eficiente que ya no implique el uso de un campo o método en particular.
Para que todo lo demás sea igual, no deberías tener ningún miembro protegido en absoluto. Pero dicho eso, si tienes muy pocos, entonces tu clase no se puede usar como una súper clase, o al menos no como una súper clase eficiente. A menudo te enteras después del hecho. Mi filosofía es tener el menor número posible de miembros protegidos la primera vez que escribas la clase. Luego trata de subclasificarlo. Puede descubrir que sin un método protegido particular, todas las subclases tendrán que hacer algo malo.
Como ejemplo, si mira
AbstractList
, encontrará que hay un método protegido para eliminar un rango de la lista en una sola toma (removeRange
). ¿Por qué está ahí? Debido a que el modismo normal para eliminar un rango, basado en la API pública, es llamar asubList
para obtener una sub-List
, y luego llamar aclear
esa sub-List
. Sin este método protegido en particular, sin embargo, lo único que podría hacer es eliminar elementos individuales en repetidas ocasiones.Piénsalo. Si tiene una representación de matriz, ¿qué hará? Colapsará repetidamente la matriz, haciendo que la orden N trabaje 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 que cualquier implementación que pueda eliminar eficientemente un rango completo lo haga. Y cualquier implementación de
List
razonable puede eliminar un rango de manera más eficiente, todo a la vez.Que necesitaríamos este método protegido es algo que tendrías que ser mucho más inteligente que yo para saber de antemano. Básicamente, implementé la cosa. Luego, cuando empezamos a subclasificarlo, 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. Agregue el menor número posible y luego agregue más según sea necesario. Los métodos protegidos representan compromisos con los diseños que puede querer cambiar. Siempre puede agregar métodos protegidos, pero no puede eliminarlos.
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 alterar sus invariantes de datos. Si le das a alguien más acceso a algunos datos internos, tienen dominio libre sobre ellos.
Versión corta: rompe la encapsulación, pero es un mal necesario que debe mantenerse al mínimo.
Depende de lo que quieras. Si desea una clase rápida, entonces los datos deben estar protegidos y usar métodos protegidos y públicos. Porque creo que debe suponer que sus usuarios que derivan de su clase conocen bastante bien su clase o al menos han leído su 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 tus virtuales:
(DO#)
static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...
No puedes sellar cada clase para evitar esto.
Por supuesto, si desea una restricción en algunos de sus campos, utilice las funciones de acceso o propiedades o algo que desee y haga que ese campo sea privado porque no hay otra solución ...
Pero personalmente no me gusta atenerme a los principios del pánico a toda costa. Especialmente haciendo propiedades con el único propósito de hacer que los miembros de datos sean privados.
(DO#):
private _foo;
public foo
{
get {return _foo;}
set {_foo=value;}
}
Esta fue mi opinión personal.
Pero haz lo que tu jefe requiera (si quiere campos privados que hacer eso).
Utilizo variables / atributos protegidos dentro de las clases base que sé que no planeo cambiar a métodos. De esta forma, las subclases tienen acceso completo a sus variables heredadas y no tienen la sobrecarga (creada artificialmente) de atravesar getters / setters para acceder a ellas. Un ejemplo es una clase que utiliza una secuencia de E / S subyacente; hay pocas razones para no permitir que las subclases tengan acceso directo a la transmisión subyacente.
Esto está bien para las variables miembro que se usan 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 privada y se pueden proporcionar getters / setters públicos / protegidos en su lugar. Un ejemplo es un mecanismo de búfer interno proporcionado por la clase base, donde el acceso a los buffers directamente desde una subclase comprometería la integridad de los algoritmos utilizados por la clase base para gestionarlos.
Es una decisión de juicio de diseño, basada en cuán simple es la variable miembro y cómo se espera que sea en futuras versiones.
La encapsulación es genial, pero se puede llevar demasiado lejos. He visto clases cuyos métodos privados accedieron a sus variables miembro utilizando 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?
La palabra clave protected
es un error conceptual y un error de diseño del lenguaje, y varios lenguajes modernos, como Nim y Ceilán (vea http://ceylon-lang.org/documentation/faq/language-design/#no_protected_modifier ), que han sido cuidadosamente diseñado en lugar de solo copiar errores comunes, no tiene esa palabra clave.
No es un miembro protegido el que rompe la encapsulación, está exponiendo miembros que no deberían exponerse y que rompe la encapsulación ... no importa si están protegidos o son públicos. El problema con la protected
es que es errónea y engañosa ... declarar que los miembros están protected
(en lugar de private
) no los protege, sino que hace lo contrario, exactamente como lo hace el public
. 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, al igual que en el caso del public
. La idea de "protegido" es una tontería ... la encapsulación no es seguridad, y la palabra clave solo promueve la confusión entre los dos. Puede 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, luego hacerlo privado o interno para su paquete, módulo, ensamblaje, etc. Si es una parte inmutable de la semántica de la clase, hágalo público, y luego no molestará a los usuarios de su clase que puedan ver que hay un miembro útil en la documentación pero puede '' t usarlo, a menos que estén creando sus propias instancias y puedan acceder a él mediante subclases.