¿Por qué el Clean Code sugiere evitar las variables protegidas?

160

Clean Code sugiere evitar las variables protegidas en la sección "Distancia vertical" del capítulo "Formato":

  

Los conceptos que están estrechamente relacionados deben mantenerse verticalmente cerca uno del otro. Claramente, esta regla no funciona para los conceptos que pertenecen a archivos separados. Pero luego los conceptos estrechamente relacionados no deben separarse en archivos diferentes a menos que tenga una 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?

    
pregunta Matsemann 28.08.2012 - 17:04
fuente

10 respuestas

269

Las variables protegidas deben evitarse porque:

  1. Tienden a conducir a problemas con YAGNI . A menos que tenga una clase descendiente que realmente haga cosas con el miembro protegido, hágalo privado.
  2. Tienden a conducir a problemas con LSP . Las variables protegidas generalmente tienen alguna invariancia intrínseca asociada a ellas (o de lo contrario serían públicas). Luego, los herederos deben mantener esas propiedades, que las personas pueden arruinar o violar voluntariamente.
  3. Tienden a violar OCP . Si la clase base hace demasiados supuestos sobre el miembro protegido, o el heredero es demasiado flexible con el comportamiento de la clase, puede llevar a que la extensión de la clase base sea modificada por esa extensión.
  4. Tienden a conducir a la herencia por extensión más que por composición. Esto tiende a conducir a un acoplamiento más estricto, más violaciones de SRP , pruebas más difíciles y un montón de otras cosas que caen dentro la discusión 'favorecer la composición sobre la herencia'.

Pero como ves, 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 de estos problemas. Pero hay una serie de cosas que hacen que sean tratados con cuidado. Con cualquier cosa que requiera ese tipo de cuidado, la gente cometerá errores y en el mundo de la programación eso significa errores y problemas de diseño.

    
respondido por el Telastyn 28.08.2012 - 18:02
fuente
52

En realidad, es la misma razón por la que evitas los globales, solo que en una escala más pequeña. Es porque es difícil encontrar en todas partes una variable que se lee, o peor, que se escriba y que sea difícil hacer que el uso sea coherente. Si el uso es limitado, como escribirse 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 está tentado a leer y escribir una variable de forma voluntaria en varios archivos, es mejor encapsularla.

    
respondido por el Karl Bielefeldt 28.08.2012 - 18:06
fuente
26

No he leído el libro, pero puedo probar lo que quiso decir el tío Bob.

Si pones protected en 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. Poner protected 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 se produce cuando crea una variable public en una clase ordinaria.

Para corregir el problema, puede encapsular la variable en una propiedad protegida, de este modo:

protected string Name
{
    get { return name; }
    private set { name = value; }
}

Esto permite que name se establezca de manera segura desde una clase derivada usando un argumento de constructor, sin exponer los detalles de implementación de la clase base.

    
respondido por el Robert Harvey 28.08.2012 - 18:05
fuente
17

El argumento del tío Bob es principalmente uno de distancia: si tienes un concepto que es importante para una clase, agrupa el concepto junto con la clase en ese archivo. No separados en dos archivos en el disco.

Las variables de los miembros protegidos se encuentran dispersas en dos lugares y, en cierto modo, se parecen a la magia. Hace referencia a esta variable, pero no está definida aquí ... donde está está definida? Y así comienza la caza. Es mejor evitar protected por completo, su argumento.

Ahora no creo que la regla deba obedecerse a la letra todo el tiempo (como: no debes usar protected ). Mire el espíritu de lo que está obteniendo en ... agrupar cosas relacionadas en un solo archivo: use las técnicas de programación y las funciones para hacerlo. Le recomendaría que no analice en exceso y que quede atrapado en los detalles de esto.

    
respondido por el J. Polfer 28.08.2012 - 23:23
fuente
9

Algunas respuestas muy buenas 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) colocar cada clase en su propio archivo (por favor, no se preocupe por C ++ donde a menudo tiene dos archivos).

Por lo tanto, es virtualmente imposible mantener las funciones en una clase derivada accediendo a una variable protegida de una clase base "verticalmente cerca" en el código fuente a la definición de la variable. Pero lo ideal es que se mantengan allí, ya que las variables protegidas están diseñadas para uso interno, lo que hace que las funciones que las acceden a menudo sean "un concepto estrechamente relacionado".

    
respondido por el Doc Brown 28.08.2012 - 18:44
fuente
4

La idea básica es que un "campo" (variable de nivel de instancia) que se declara protegido es probablemente más visible de lo que debe ser, y menos "protegido" de lo que le gustaría. No hay un modificador de acceso en C / C ++ / Java / C # que sea equivalente a "accesible solo por clases secundarias dentro del mismo ensamblaje", lo que le otorga la posibilidad de definir sus propios hijos que pueden acceder al campo en su ensamblaje, pero no permitir que los niños creados en otras asambleas tengan el mismo acceso; C # tiene modificadores internos y protegidos, pero su combinación hace que el acceso sea "interno o protegido", no "interno y protegido". Por lo tanto, un campo que está protegido es accesible para cualquier niño, ya sea que usted escribió que el niño o alguien más lo hizo. Protegido es, por lo tanto, una puerta abierta a un hacker.

Además, los campos por su definición no tienen prácticamente ninguna validación inherente para cambiarlos. en C # puede hacer uno solo de lectura, lo que hace que los tipos de valor sean constantes y los tipos de referencia no puedan reinicializarse (pero aún así son muy mutables), pero eso es todo. Como tal, incluso protegidos, sus hijos (en los que no puede confiar) tienen acceso a este campo y pueden configurarlo como algo no válido, lo que hace que el estado del objeto sea inconsistente (algo que debe evitarse).

La forma aceptada de trabajar con los campos es hacerlos privados y acceder a ellos con una propiedad y / o un método de obtención y establecimiento. Si todos los consumidores de la clase necesitan el valor, haga que el captador (al menos) sea público. Si solo los niños lo necesitan, proteja al receptor.

Otro enfoque que responde a la pregunta es preguntarse; ¿Por qué el código en un método secundario necesita la capacidad de modificar mis datos de estado directamente? ¿Qué dice eso de ese código? Ese es el argumento de "distancia vertical" en su cara. Si hay un código en un hijo que debe alterar directamente el estado principal, entonces tal vez ese código debería pertenecer al padre en primer lugar.

    
respondido por el KeithS 29.08.2012 - 03:35
fuente
3

Es una discusión interesante.

Para ser franco, 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 del software, algo en lo que está trabajando el proyecto Light Table (http://www.chris-granger.com/2012/04/12/light- tabla --- 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, lo que hizo la clase padre.

En mi opinión, las jerarquías de clase deberían ser lo más superficiales posibles, 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 se puedan proteger con no . Si la clase extendida debería representar una extensión que incluya todo el comportamiento de la clase padre (lo cual creo que debería y, por lo tanto, cree que los métodos privados son intrínsecamente malos), ¿por qué la clase extendida no representa el almacenamiento de dichos datos también?

Para decirlo de otra manera: en cualquier caso en el que sienta que una variable privada se adaptaría mejor que una protegida, la herencia no es la solución al problema en primer lugar.

    
respondido por el spronkey 29.08.2012 - 08:34
fuente
0

Como dice allí, esta es solo una de las razones, es decir, el código vinculado lógicamente se debe colocar 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 hace consultas de base de datos dentro del paquete con clases que muestran la interfaz de usuario.

Sin embargo, la razón principal por la que no se recomiendan las variables protegidas es porque tienden a romper la encapsulación. Las variables y los métodos deben tener una visibilidad lo más limitada posible; para mayor referencia, vea Effective Java de Joshua Bloch, punto 13 - "Minimizar la accesibilidad de las clases y los miembros".

Sin embargo, no debes tomar todo este anuncio litteram, solo como guildline; si las variables protegidas son muy malas, en primer lugar no se habrían colocado dentro del idioma. Un lugar razonable para los campos protegidos es imho dentro de la clase base desde un marco de prueba (las clases de prueba de integración lo extienden).

    
respondido por el m3th0dman 28.08.2012 - 22:43
fuente
0

Encuentro que usar variables protegidas para las pruebas JUnit puede ser útil. Si los hace privados, no podrá acceder a ellos durante las pruebas sin reflexión. Esto puede ser útil si está probando un proceso complejo a través de muchos cambios de estado.

Podrías hacerlos privados, con métodos getter protegidos, pero si las variables solo se van a usar externamente durante una prueba de JUnit, no estoy seguro de que esa sea una mejor solución.

    
respondido por el Nick 29.08.2012 - 11:07
fuente
-1

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 se debe evitar.

Creo que el problema con el modificador protegido es que el miembro no solo está expuesto a las subclases, sino que también es visible para todo el paquete. Creo que es este aspecto dual al que el tío Bob está haciendo una excepción aquí. Por supuesto, no siempre se puede evitar tener métodos protegidos y, por lo tanto, la calificación de la variable protegida.

Creo que un enfoque más interesante es hacer que todo sea público, que te obligará a tener clases pequeñas, lo que a su vez hace que toda la métrica de distancia vertical sea un tanto discutible.

    
respondido por el CurtainDog 28.08.2012 - 22:32
fuente

Lea otras preguntas en las etiquetas