La importancia del encapsulamiento. Parte 3. Descriptores de acceso. Controlando la forma de acceder al estado.
Descriptores de acceso. Controlando la forma de acceder al estado.
Que una clase permita leer un valor a otra clase que lo requiera no quiere decir que lo pueda modificar. Y si una clase debe poder modificarlo no significa tampoco que lo pueda hacer de forma directa, sino que quizás debería utilizar descriptores de acceso (assessors), también llamados getters and setters o propiedades en .Net.Usar propiedades es una buena práctica incluso cuando el acceso es directo, es decir, aunque lo único que hagamos sea devolver un valor en el get o asignar un valor en el set. Quién sabe si en un futuro vamos a necesitar un descriptor de acceso con implementación. En este caso sólo tendríamos que implementar la lógica en el get o en el set sin alterar la compatibilidad binaria. Para explicarlo mejor vamos a ver un ejemplo simple del uso de un descriptor de acceso.
Manos a la obra
Imaginemos que tenemos el campo precio que nos guarda un valor de una cantidad de dinero. Si optamos por hacerla público el campo daremos acceso desde el exterior directamente al valor (estado interno del objeto), tanto de lectura como de escritura.Haciendo esto estamos definiendo para el mundo exterior una interfaz de acceso directo a nuestro valor, es decir, nos hemos comprometido a brindar este acceso para siempre (siempre que queramos respetar la compatibilidad binaria). En caso de que queramos aplicar una lógica de acceso a este valor proporcionando un descriptor de acceso, tendríamos que cambiar la interfaz y por consiguiente tendríamos de cambiar todos los clientes que usan nuestra clase y recompilar de nuevo sus ensamblados.
Encapsulando el campo
Para encapsular la variable la hacemos privada e implementamos una propiedad cuyo get y set establecen la lógica de acceso a este valor. A la variable privada también se le conoce con el nombre de variable de respaldo.Desde Net 3.0 podemos utilizar una sintaxis mucho más cómoda para este tipo de propiedades que no tienen lógica en sus accesos. Se trata de las propiedades auto-implementadas. El compilador se encargará de crear el campo de “respaldo” de forma transparente para nosotros:
¿Y todo esto para qué?
Ahora imaginemos que debido a un cambio en los criterios de diseño se establece una validación en la que si se intenta escribir una cantidad que sobrepasa un cierto valor debemos lanzar una excepción. Además, si el precio es negativo debemos guardar un 0.Al tener una propiedad podemos fácilmente agregar esta lógica:
Es decir, se trata de separar el “respaldo” de:
- la forma en que permitimos modificarla (set) y/o
- la forma en que servimos el valor al llamante (get)
Accesos restringidos
¿Por qué tener el mismo nivel de visibilidad para la lectura que para la escritura?Tenemos varias formas de romper esta simetría. No tenemos por qué implementar los dos accesos de una propiedad. Por ejemplo, la siguiente propiedad es de sólo lectura:
Y la siguiente de sólo escritura:
También tenemos los accesos con distinto nivel de lectura y escritura. Por ejemplo, la siguiente propiedad tiene un get y un set, pero el set está limitado por el modificador
protected
, lo que restringe su acceso sólo para la propia clase y sus extensiones. Desde otras clases se comportará como una propiedad de sólo lectura.A parte de todo esto, también existen algunas otras razones más para decantarse por propiedades antes de utilizar campos. No voy a entrar a analizar cada una de ellas pero las dejo aquí por si alguien todavía no ve razones suficientes:
- La reflexión es mucho más simple usando propiedades que campos. Así que si piensas trabajar con reflexión implementa mejor propiedades;
- No puedes enlazar (Databind) contra campos. Sólo funcionan con propiedades;
- Puedes implementar inicialización diferida (lazy initialization).
¿Cuándo tiene sentido usar campos públicos?
Hasta el propio framework de .Net utiliza en algunas ocasiones campos públicos en lugar de propiedades. Si sabemos seguro que un valor nunca va a tener una lógica de acceso podemos permitir su acceso directo. Tal es el caso de los campos declarados como <code>const</code> o <code>readonly</code>, como por ejemplo el campo <code>string.Empty</code> del Framework o el campo <code>Math.PI</code>. No tiene mucho sentido aplicarle una lógica a su acceso de lectura. Y al ser constantes de sólo lectura nunca va a existir tampoco una lógica de acceso de escritura.>> Ir a Parte 4. Encapsular constructores.
<< Volver a Parte 2. Un ejemplo de encapsulamiento en la vida real.
Written on October 17, 2017