2009-07-13 24 views
8

Actualmente estoy leyendo Code Complete donde McConnell recomienda encarecidamente que todas las variables sean privadas. Por casualidad, estaba trabajando en un proyecto en el que necesitaba cambiar una variable privada.variables privadas o protegidas?

La clase tenía una variable privada (a String) diciéndole dónde cargar una imagen para utilizarla en el sistema chrome. Necesitaba cambiar esta imagen, no sé sobre otros idiomas, pero hasta donde sé en Flex/AIR, no hay forma de anular una variable privada.

Si hubiera sido declarado protegido, podría simplemente haber extendido la clase y anular esa variable. Pero como era privado, tuve que copiar todo el código de la clase y crear una clase duplicada con la única diferencia de esa cadena.

Creo que el argumento es utilizar privado, ya que hace que el acoplamiento entre las superclases y las subclases sea más flexible, sin embargo tuve que violar por completo DRY para poder lograr un cambio de cadena simple, lo cual me parece peor.

Esto me hace pensar que lo protegido es mejor que lo privado. Sin embargo, quiero hacer las cosas de la mejor manera correcta de prácticas. Entonces, si lo privado es mejor, quiero entender por qué.

Si el consenso general es que lo privado es mejor, ¿alguien puede explicar por qué?

Respuesta

26

En este caso, la ubicación de esa imagen solía ser una característica privada, específica de la implementación de la clase base. Sus nuevos requisitos significaban que debía poder variar de una clase derivada a otra.

Debe mantener el campo miembro privado, pero definir una propiedad virtual protegida para exponerlo a las clases derivadas:

private const string _defaultImagePath = @"C:\whatever.bmp"; 

protected virtual string ImagePath { 
    get {return _defaultImagePath;} 
} 

en la clase derivada que quiere cambiarlo:

private const string _myImagePath = @"C:\other.bmp"; 
protected override string ImagePath { 
    get {return _myImagePath;} 
} 

Usted también querrá cambiar la clase base para que use la propiedad cuando necesite la ruta de la imagen, en lugar de usar el campo. Esta es la refactorización de "Encapsulate Field".

+0

Sí, exactamente. No toque la variable principal, anule el getter en su lugar. Mucho más encapsulado y menos propenso a errores. – mikek

4

Realice las variables private para cuando sepa solo que la clase en particular en la que están definidas será la única que haga uso de esas variables. Sin embargo, si está buscando extender una clase, debe usar protected. Privado es realmente así que no está utilizando variables globalmente (por lo que utiliza getters y setters en clases) que ayuda a aflojar el acoplamiento. Protegido es perfectamente aceptable cuando se extiende una clase.

En general, yo diría que use el tipo de acceso que más oculta la variable de las clases y/o paquetes externos.

0

Todo lo que es parte de la implementación de la clase debe ser privado, eso le da la libertad de cambiarlo, si fuera necesario.

En el ejemplo que mencionaste, tal vez la interfaz de la clase no se definió completamente porque necesitas cambiar una propiedad privada de la clase. Si tiene el control del código, puede proteger la propiedad o agregar funciones para obtener y establecer el valor.

1

"La mejor práctica" es en sí misma un término subjetivo, mientras que hay muchas convenciones que sirven para mejorar el código, el valor de cada una debe contrastarse con los requisitos del problema que está tratando de resolver.

La declaración de esa variable como privada debe tomarse como una expresión de las intenciones de desarrollo de la variable (es decir, el uso interno), lo que significa que o bien debe encontrar otro punto de extensibilidad o código duplicado.

Desde una perspectiva de diseño de la API, se debe usar privada para aislar detalles de la implementación de las personas que llaman y protegido para exponer las cosas para los herederos

2

Si se hubiera declarado protegido, que podría simplemente haber ampliado la clase, y anuló esa variable. Pero ya que era privado, tuve que copiar todo el código de la clase y crear una clase duplicada con la única diferencia siendo esa cadena.

Y si hubiera habido un colocador para la ubicación de la imagen, no habría necesitado una subclase (o una nueva clase) en absoluto.

Y si en lugar de querer cambiar la imagen, hubieras querido algún otro cambio menor, entonces tal vez hacer alguna otra API protegida/virtual podría haber ayudado. O tal vez no. Mi único punto es que es difícil ver un ejemplo nebuloso como este y deducir sabiduría general de él.

Existe un intercambio entre la flexibilidad que le da una clase en términos de su comportamiento y extensibilidad, y cuánto esfuerzo se necesita para diseñar y evaluar la clase (y otras interacciones entre cómo comprensible, bien documentado, etc. . la clase es). Estas concesiones de diseño no pueden evaluarse en el vacío, sino que deben evaluarse en contexto. ¿Cuán probable es que las diversas características/comportamientos/extensiones/cambios que podría admitir una clase posiblemente admitan actualmente? La clase bien diseñada es aquella que tiene extensiones para escenarios comunes necesarios y carece de extensiones y API innecesarias. (Y para las extensiones que están allí, hay una opción entre los 'setters' simples y la extensibilidad más compleja protegida/virtual/de subclases ... esto último no es una desventaja para optar por algo ligero.)

I Don ' Sé que esto responde bien a tu pregunta específica (o no), pero por la forma en que leí tu pregunta, me hizo sentir que insinuabas que "es mejor que las clases sean cuchillos suizos porque la extensibilidad/personalización es buena". ", y quería decir que estas 'buenas' cosas vienen con compensaciones. Además, su pregunta parecía implicar que esta situación era "privada" versus "protegida" y "subtublicada", mientras que creo que un simple "método de establecimiento" sería un cambio de API suficiente.

1

Privado es, IMO, siempre lo mejor al principio, y luego hacer público/protegido cuando sea necesario. Es mucho más fácil crear un nuevo método que intentar eliminar la funcionalidad de la que alguien dependía.

En este caso, creo que el mejor enfoque habría sido simplemente haber incluido un colocador. ¿Hubo alguna otra funcionalidad que tu subclase tuviese que proporcionar, o podrías haber creado una nueva instancia, establecer la ruta y terminar con ella?

0

Hazlo privado hasta que necesite protección. Este es un problema de abstracción con lenguajes como C#, C++ y Java donde protected/private está vinculado a la implementación. Los miembros privados se pueden incluir automáticamente de manera segura, mientras que los protegidos no pueden. Esto es similar al problema de abstracción en C# y C++ con monomorfismo y polimorfismo que necesitan la palabra clave virtual.

Esto esencialmente hace un problema de clase base frágil.Definir todo como protegido/virtual es lo más seguro en términos de reutilización, pero dificulta las optimizaciones. Definir todo como privado/no virtual es lo más eficiente.

En un lenguaje como Eiffel, el compilador detecta automáticamente si las funciones pueden estar en línea y detecta automáticamente si una característica es polimórfica o monomórfica. http://dev.eiffel.com

2

Cuando comencé por primera vez en OO, solía hacer todo public o protected, pensando: "¿Por qué debería restringir lo que otras personas querían hacer con mi código?"

Como tengo más experiencia, sucedieron dos cosas:

  1. IDE modernos llegaron a lo largo de, por lo que es ridículamente fácil para cambiar el código .
  2. Tuve que mantener un montón de código desagradable que se había extendido en formas en que sus diseñadores no habían querido.

Ahora mi enfoque es: hacer que private hasta que se necesita para que sea public (o protected) - e incluso entonces, pensar mucho acerca de si eso es realmente la solución correcta.

Un buen método protected es un intencional punto de extensibilidad, puesto por el diseñador. Descubrí que diseñar para la extensibilidad es realmente difícil, y tienes que do, si solo lo dejas abierto y esperas, estás pidiendo pesadillas de mantenimiento en el futuro.

-1

Puedo ser el único aquí que esté de acuerdo con lo que creo que el autor está implicando. La idea de tener que abrir una superclase para realizar cambios en un descriptor de acceso puede no ser simple y no siempre es una opción. El método privado hasta que necesita ser protegido es absurdo y, en última instancia, significa que, en primer lugar, no tenía motivos para usar el servicio privado.

La verdadera razón por la que desea mantener un miembro privado es para que una instancia de una subclase no pueda tener acceso inesperado a los miembros de una instancia de una superclase que debe permanecer bloqueada. El desafortunado efecto secundario es la falta de extensibilidad. La solución más votado por John Sanders es probablemente la mejor práctica porque aborda este problema.

Sin embargo, he logrado codificar miles de clases con el uso frecuente de variables protegidas (y casi no privadas) y nunca he tenido nada que pueda llamar un problema con el uso inesperado. Parece que lo mismo se puede lograr simplemente eligiendo no codificar de una manera en la que estás jugando con otros valores protegidos de clases.

Así que en conclusión, me dicen:

  • Sigue codificación que utiliza protegida pero recuerda que esto abre los datos sensibles a las subclases.
  • Disfrute de los beneficios de la extensibilidad fácil, pero recuerde mantener una relación 'es-a' entre sus superclases y subclases para que el comportamiento de cada uno se mantenga fiel a la intensión del método.
  • Utilice la técnica de captador de variable/protegido privado si es duro o si necesita mayor seguridad.
0

Los miembros protegidos forman parte de la API, ya que las clases derivadas de otros paquetes (es decir, paquetes que están fuera del control de su organización) pueden verlos. Como tales, representan un compromiso. El consejo de preferir cantidades privadas a consejos para evitar hacer compromisos innecesarios. Siempre puede hacer que un miembro menos visible sea más visible, pero no siempre puede hacer lo contrario.

Cuestiones relacionadas