Clean Code sugiere evitar variables protegidas en la sección "Distancia vertical" del capítulo "Formateo":
Los conceptos que están estrechamente relacionados deben mantenerse verticalmente cerca uno del otro. Claramente, esta regla no funciona para conceptos que pertenecen a archivos separados. Pero los conceptos estrechamente relacionados no deben separarse en archivos diferentes a menos que tenga una muy buena razón. De hecho, esta es una de las razones por las que se deben evitar las variables protegidas .
¿Cuál es el razonamiento?
code-quality
clean-code
variables
Matsemann
fuente
fuente
Respuestas:
Las variables protegidas deben evitarse porque:
Pero como puede ver, todos estos son 'tienden a'. A veces, un miembro protegido es la solución más elegante. Y las funciones protegidas tienden a tener menos problemas. Pero hay una serie de cosas que hacen que se traten con cuidado. Con cualquier cosa que requiera ese tipo de cuidado, las personas cometerán errores y en el mundo de la programación eso significa errores y problemas de diseño.
fuente
Es realmente la misma razón por la que evitas los globales, solo que a menor escala. Es porque es difícil encontrar en todas partes una variable que se está leyendo, o peor aún, escrita, y es difícil hacer que el uso sea consistente. Si el uso es limitado, como escribir en un lugar obvio en la clase derivada y leer en un lugar obvio en la clase base, las variables protegidas pueden hacer que el código sea más claro y conciso. Si tiene la tentación de leer y escribir una variable willy-nilly en varios archivos, es mejor encapsularla.
fuente
No he leído el libro, pero puedo entender lo que el tío Bob quería decir.
Si te pones
protected
algo, eso significa que una clase puede heredarlo. Pero se supone que las variables miembro pertenecen a la clase en la que están contenidas; Esto es parte de la encapsulación básica. Ponerprotected
en una variable miembro rompe la encapsulación porque ahora una clase derivada tiene acceso a los detalles de implementación de la clase base. Es el mismo problema que ocurre cuando haces una variablepublic
en una clase ordinaria.Para corregir el problema, puede encapsular la variable en una propiedad protegida, así:
Esto permite
name
establecer de forma segura desde una clase derivada utilizando un argumento constructor, sin exponer detalles de implementación de la clase base.fuente
protected
ypublic
miembros. SiBaseType
expone a un miembro público, eso implica que todos los tipos derivados deben tener el mismo miembro funcionando de la misma manera, ya que unaDerivedType
instancia se puede pasar al código que recibe unaBaseType
referencia y espera usar ese miembro. Por el contrario, siBaseType
expone a unprotected
miembro,DerivedType
puede esperar acceder a ese miembrobase
, perobase
no puede ser otra cosa que aBaseType
.El argumento del tío Bob es principalmente de distancia: si tiene un concepto que es importante para una clase, agrupe el concepto junto con la clase en ese archivo. No separados en dos archivos en el disco.
Las variables miembro protegidas se encuentran dispersas en dos lugares y, de alguna manera, parece magia. Hace referencia a esta variable, pero no está definida aquí ... ¿dónde está definida? Y así comienza la caza. Mejor evitar por
protected
completo, su argumento.Ahora no creo que esa regla deba ser obedecida al pie de la letra todo el tiempo (como: no debes usarla
protected
). Mire el espíritu de lo que está llegando ... agrupe cosas relacionadas en un solo archivo; use técnicas y características de programación para hacer eso. Le recomendaría que no analice en exceso y se quede atrapado en los detalles sobre esto.fuente
Algunas muy buenas respuestas ya están aquí. Pero una cosa para agregar:
En la mayoría de los lenguajes OO modernos, es un buen hábito (en Java AFAIK es necesario) poner cada clase en su propio archivo (por favor, no entienda acerca de C ++, donde a menudo tiene dos archivos).
Por lo tanto, es prácticamente imposible mantener las funciones en una clase derivada accediendo a una variable protegida de una clase base "verticalmente cercana" en código fuente a la definición de la variable. Pero idealmente deberían mantenerse allí, ya que las variables protegidas están destinadas al uso interno, lo que hace que las funciones que las acceden a menudo sean "un concepto estrechamente relacionado".
fuente
La idea básica es que un "campo" (variable de nivel de instancia) que se declara como protegido es probablemente más visible de lo que debe ser, y menos "protegido" de lo que quisiera. No hay un modificador de acceso en C / C ++ / Java / C # que sea equivalente a "accesible solo para las clases secundarias dentro del mismo ensamblado", lo que le permite definir sus propios hijos que pueden acceder al campo en su ensamblaje, pero no permitir que los niños creados en otros ensamblados tengan el mismo acceso; C # tiene modificadores internos y protegidos, pero combinarlos hace que el acceso sea "interno o protegido", no "interno y protegido". Por lo tanto, cualquier niño puede acceder a un campo protegido, ya sea que usted haya escrito ese niño o alguien más. Protegido es, por lo tanto, una puerta abierta a un hacker.
Además, los campos, por su definición, prácticamente no tienen validación inherente a su cambio. en C #, puede hacer uno de solo lectura, lo que hace que los tipos de valor sean efectivamente constantes y los tipos de referencia no puedan reinicializarse (pero aún son muy mutables), pero eso es todo. Como tal, incluso protegido, sus hijos (en los que no puede confiar) tienen acceso a este campo y pueden configurarlo como algo no válido, haciendo que el estado del objeto sea inconsistente (algo que debe evitarse).
La forma aceptada de trabajar con campos es hacerlos privados y acceder a ellos con una propiedad y / o un método getter y setter. Si todos los consumidores de la clase necesitan el valor, haga público el captador (al menos). Si solo los niños lo necesitan, proteja al captador.
Otro enfoque que responde a la pregunta es preguntarse a sí mismo; ¿Por qué el código en un método secundario necesita la capacidad de modificar mis datos de estado directamente? ¿Qué dice eso sobre ese código? Ese es el argumento de la "distancia vertical" en su cara. Si hay un código en un elemento secundario que debe alterar directamente el estado primario, ¿tal vez ese código debería pertenecer al elemento primario en primer lugar?
fuente
Es una discusión interesante.
Para ser sincero, las buenas herramientas pueden mitigar muchos de estos problemas "verticales". De hecho, en mi opinión, la noción de "archivo" es en realidad algo que dificulta el desarrollo de software, algo en lo que está trabajando el proyecto Light Table (http://www.chris-granger.com/2012/04/12/light- table --- a-new-ide-concept /).
Saque los archivos de la imagen, y el alcance protegido se vuelve mucho más atractivo. El concepto protegido se vuelve más claro en idiomas como SmallTalk, donde cualquier herencia simplemente extiende el concepto original de una clase. Debería hacer todo, y tener todo, que hizo la clase padre.
En mi opinión, las jerarquías de clase deberían ser lo más superficiales posible, con la mayoría de las extensiones provenientes de la composición. En tales modelos, no veo ninguna razón para que las variables privadas no estén protegidas. Si la clase extendida debería representar una extensión que incluye todo el comportamiento de la clase padre (lo cual creo que debería, y por lo tanto creo que los métodos privados son inherentemente malos), ¿por qué la clase extendida no debería representar también el almacenamiento de dichos datos?
Para decirlo de otra manera: en cualquier caso en el que creo que una variable privada se adaptaría mejor que una protegida, la herencia no es la solución al problema en primer lugar.
fuente
Como dice allí, esta es solo una de las razones, es decir, el código lógicamente vinculado debe colocarse dentro de las entidades físicas vinculadas (archivos, paquetes y otros). En una escala más pequeña, esta es la misma razón por la que no coloca una clase que realiza consultas DB dentro del paquete con clases que muestran la interfaz de usuario.
Sin embargo, la razón principal por la que no se recomiendan variables protegidas es porque tienden a romper la encapsulación. Las variables y los métodos deben tener la visibilidad más limitada posible; para más información, consulte Java eficaz de Joshua Bloch , elemento 13: "Minimice la accesibilidad de clases y miembros".
Sin embargo, no debes tomar todo este ad litteram, solo como una línea de guild; Si las variables protegidas son muy malas, en primer lugar no se habrían colocado dentro del lenguaje. Un lugar razonable para los campos protegidos en mi humilde opinión es dentro de la clase base desde un marco de prueba (las clases de prueba de integración lo extienden).
fuente
Creo que usar variables protegidas para las pruebas JUnit puede ser útil. Si los hace privados, no podrá acceder a ellos durante la prueba sin reflexión. Esto puede ser útil si está probando un proceso complejo a través de muchos cambios de estado.
Podría hacerlos privados, con métodos getter protegidos, pero si las variables solo se van a usar externamente durante una prueba JUnit, no estoy seguro de que sea una mejor solución.
fuente
Sí, estoy de acuerdo en que es algo extraño dado que (como recuerdo) el libro también analiza todas las variables no privadas como algo que debe evitarse.
Creo que el problema con el modificador protegido es que el miembro no solo está expuesto a las subclases, sino que también está visible para todo el paquete. Creo que es este aspecto dual del que tío Bob está haciendo una excepción aquí. Por supuesto, uno no siempre puede evitar tener métodos protegidos y, por lo tanto, la calificación de variable protegida.
Creo que un enfoque más interesante es hacer que todo sea público, eso te obligará a tener clases pequeñas, lo que a su vez hace que la métrica de distancia vertical sea algo discutible.
fuente