Nuestros mejores investigadores han agotado sus depósitos de café, en su búsqueda todo el tiempo por la solución, hasta que Saúl halló la respuesta en Bitbucket por lo tanto en este momento la compartimos con nosotros.
Solución:
En realidad hay muchas buenas razones considerar el uso de descriptores de acceso en lugar de exponer directamente los campos de una clase, más allá del argumento de la encapsulación y facilitar los cambios futuros.
Estas son algunas de las razones que conozco:
- Encapsulación del comportamiento asociado con la obtención o configuración de la propiedad: esto permite agregar funciones adicionales (como la validación) más fácilmente más adelante.
- Ocultar la representación interna de la propiedad mientras se expone una propiedad utilizando una representación alternativa.
- Aislar su interfaz pública del cambio, permitiendo que la interfaz pública permanezca constante mientras la implementación cambia sin afectar a los consumidores existentes.
- Controlar la semántica de la gestión (eliminación) de la vida y la memoria de la propiedad, lo que es particularmente importante en entornos de memoria no administrada (como C ++ o Objective-C).
- Proporcionar un punto de interceptación de depuración para cuando una propiedad cambia en tiempo de ejecución: depurar cuándo y dónde una propiedad cambió a un valor particular puede ser bastante difícil sin esto en algunos idiomas.
- Interoperabilidad mejorada con bibliotecas que están diseñadas para operar contra captadores / definidores de propiedades: me vienen a la mente Mocking, Serialization y WPF.
- Permitir que los herederos cambien la semántica de cómo se comporta y se expone la propiedad anulando los métodos getter / setter.
- Permitir que el getter / setter se pase como expresiones lambda en lugar de valores.
- Los getters y setters pueden permitir diferentes niveles de acceso; por ejemplo, el get puede ser público, pero el conjunto podría estar protegido.
Porque dentro de 2 semanas (meses, años) cuando te des cuenta de que tu armadora necesita hacer más además de establecer el valor, también se dará cuenta de que la propiedad se ha utilizado directamente en otras 238 clases 🙂
Un campo público no es peor que un par getter / setter que no hace nada más que devolver el campo y asignarle. Primero, está claro que (en la mayoría de los idiomas) no hay diferencia funcional. Cualquier diferencia debe estar en otros factores, como facilidad de mantenimiento o legibilidad.
Una ventaja que se menciona a menudo de los pares getter / setter, no lo es. Existe la afirmación de que puede cambiar la implementación y no es necesario volver a compilar sus clientes. Supuestamente, los establecedores le permiten agregar funcionalidades como la validación más adelante y sus clientes ni siquiera necesitan saberlo. Sin embargo, agregar validación a un establecedor es un cambio en sus condiciones previas, una violación del contrato anterior, que era, simplemente, “puedes poner cualquier cosa aquí, y puedes obtener lo mismo más tarde del getter”.
Entonces, ahora que rompió el contrato, cambiar cada archivo en la base de código es algo que debe querer hacer, no evitar. Si lo evita, está asumiendo que todo el código asumió que el contrato para esos métodos era diferente.
Si ese no debería haber sido el contrato, entonces la interfaz permitía a los clientes poner el objeto en estados no válidos. Eso es exactamente lo opuesto a la encapsulación. Si ese campo realmente no se pudo establecer en nada desde el principio, ¿por qué no estuvo allí la validación desde el principio?
Este mismo argumento se aplica a otras supuestas ventajas de estos pares de getter / setter de transferencia: si luego decide cambiar el valor que se establece, está rompiendo el contrato. Si anula la funcionalidad predeterminada en una clase derivada, más allá de algunas modificaciones inofensivas (como el registro u otro comportamiento no observable), está rompiendo el contrato de la clase base. Eso es una violación del principio de sustituibilidad de Liskov, que se considera uno de los principios de OO.
Si una clase tiene estos captadores y definidores tontos para cada campo, entonces es una clase que no tiene invariantes en absoluto, sin contrato. ¿Es eso realmente un diseño orientado a objetos? Si todo lo que tiene la clase son esos captadores y establecedores, es solo un portador de datos tonto, y los titulares de datos tontos deberían verse como titulares de datos tontos:
class Foo
public:
int DaysLeft;
int ContestantNumber;
;
Agregar pares de captador / definidor de paso a través a dicha clase no agrega valor. Otras clases deberían proporcionar operaciones significativas, no solo operaciones que los campos ya proporcionan. Así es como puede definir y mantener invariantes útiles.
Cliente: “¿Qué puedo hacer con un objeto de esta clase?”
Diseñador: “Puede leer y escribir varias variables”.
Cliente: “Oh … genial, ¿supongo?”
Hay razones para usar getters y setters, pero si esas razones no existen, hacer pares de getter / setter en nombre de false dioses de encapsulación no es algo bueno. Las razones válidas para hacer getters o setters incluyen las cosas que se mencionan a menudo como los cambios potenciales que puede realizar más adelante, como la validación o diferentes representaciones internas. O tal vez los clientes deberían poder leer el valor, pero no escribir (por ejemplo, leer el tamaño de un diccionario), por lo que un captador simple es una buena opción. Pero esas razones deben estar presentes cuando tome la decisión, y no solo como algo potencial que podría desear más adelante. Esta es una instancia de YAGNI (No lo vas a necesitar).
Reseñas y puntuaciones del post
Si piensas que te ha sido de utilidad este post, te agradeceríamos que lo compartas con otros juniors y nos ayudes a difundir esta información.