Getters/Setters. Perversos. Punto.

Horacio Hoyos Rodriguez
6 min readMar 13, 2019

(Esta es una traducción del blog de Yegor Bugayenko)

Los Setters y Getters son métodos de acceso a los campos/atributos de una clase.
Setters: Del Inglés Set, que significa establecer, sirven para asignar un valor a un campo/atributo.
Getters: Del Inglés Get, que significa obtener, sirven para recuperar el valor de un campo/atributo.

Hay un viejo debate, iniciado en el 2003 por Allen Holub en su famoso artículo Why getter and setter methods are evil, que discute sí los getters/setters son un antipatrón y deben evitarse, o si son algo que es inevitablemente necesario en la programación orientada a objetos. Trataré de aportar mi grano de arena a la discusión.

La esencia del siguiente texto es esta: el uso de getters y setters es una práctica terrible y aquellos que la usen no tienen excusa. Reitero, para evitar malentendidos, no estoy diciendo que get/set deben ser en lo posible evitados. No. Estoy diciendo que nunca deben estar cerca de su código.

¿Suficientemente arrogante para atrapar su atención? ¿Ha utilizado el patrón get/set por 15 años y se considera a sí mismo un arquitecto de Java respetable? ¿Y no quiere escuchar estas sandeces en boca de un extraño? Bien, entiendo cómo se siente. Yo me sentí igual cuando me encontré con el libro Object Thinking por David West, el mejor libro sobre programación orientada a objetos que he leído a la fecha. Así que por favor. Relájese un poco y trate de entender mientras yo trato de explicar.

Argumentos Existentes

Existen algunos argumentos en contra del “acceso via get/set” (otro nombre para getters y setters), en un mundo orientado a objetos. En mi opinión, ninguno es lo suficientemente fuerte. Repasémolos rápidamente.

Pida, No Pregunte
Allen Holub dice “No pregunte por la información que necesita para hacer el trabajo; pregunte al objeto que tiene la información que haga el trabajo por usted”.

Violar el Principio de Encapsulamiento
Un objeto puede ser destruido por otros objetos, dado que estos últimos pueden infectar información al primero, a través de setters. El objeto no puede encapsular su propio estado de manera segura, debido a que cualquiera lo puede alterar.

Detalles de Implementación Expuestos
Si podemos sacar un objeto de otro objeto, estamos dependiendo demasiado en los detalles de implementación del primer objeto. Si el tipo de un resultado cambia, digamos mañana, tenemos que modificar nuestro código de igual manera.

Todas estas justificaciones son razonables, pero no dejan de lado la razón principal.

Incredulidad Fundamental

La mayoría de los programadores creen que un objeto es una estructura de datos con métodos. Cito Getters and Setters Are Not Evil, un articulo por Bozhidar Bozhanov:

But the majority of objects for which people generate getters and setters are simple data holders.

Esta misconception es la consecuencia de un mal entendido épico! Los objetos no son “meros contenedores de datos”. Los objetos no son estructuras de datos a las que les añadimos métodos . El concepto de “contenedor de datos” vino a la programación orientada a objetos de lenguajes procedimentales, especialmente C y COBOL. Lo digo de nuevo: un objeto no es un set de datos y funciones para manipularlos. Un objeto no es una entidad de datos.

¿Qué es entonces?

Una Bola y Un Perro

En una programación orientada a objetos real, los objetos son criaturas vivas, como usted y yo. Son organismos vivientes, con su propio comportamiento, características y ciclo de vida.

¿Puede un organismo viviente tener un setter? ¿Se puede “set” (asignar) una bola a un perro? En realidad no. Pero es lo que indica la lógica del siguiente bloque de código :

Dog dog = new Dog();
dog.setBall(new Ball());

¿Qué tal suena eso?

¿Podemos extraer una bola de un perro? Probablemente sí, si el perro se la comió y le estamos practicando cirugía. En ese caso, sí, sí podemos “get” una bola de un perro. Esto es a lo que me refiero.

Dog dog = new Dog();
Ball ball = dog.getBall();

O un ejemplo más ridículo aún:

Dog dog = new Dog();
dog.setWeight("23kg");

¿Se pueden imaginar esta transacción en el mundo real?

¿Se parece a lo que usted está escribiendo todos los días? Si sí, entonces usted es un programador procedimental. Admítalo. Y esto es lo que David West tiene que decir al respecto, en la página 30 de su libro:

El primer paso en la transformación de un programador procedimental exitoso a un programador orientado a objetos exitoso es una lobotomía.

¿Ud. necesita una lobotomía? Verá, yo definitivamente necesitaba una y la recibí, mientras leía el libro Object Thinking, escrito pot West.

Pensando en Objetos

Empiece a pensar como un objeto e inmediatamente verá como renombrar esos métodos. Esto es lo que probablemente obtendrá:

Dog dog = new Dog();
dog.take(new Ball());
Ball ball = dog.give();

En este caso, estamos tratando al perro como un animal real, que puede tomar una bola de nosotros y la puede devolver, cuando se la pedimos. Vale la pena mencionar que el perro no puede devolvernos un `NULL`. Los perros no saben qué es `NULL` :) El pensamiento en objetos tiene como consecuencia inmediata eliminar las referencias NULAS en su código.

A parte de esto, pensar como objeto lo guiará a objetos inmutables, como en el ejemplo del “peso del perro”. Probablemente reescriba ese código así:

Dog dog = new Dog("23kg");
int weight = dog.weight();

El perro es un organismo viviente inmutable, que no le permite a nadie del mundo exterior modificar su peso, tamaño, nombre, etc. Puede, si se le pregunta, decirnos su peso o su nombre. No hay nada malo con métodos públicos que demuestran acceso a ciertos detalles “internos” de un objeto. Pero estos métodos no son “getters” y no deben tener el prefijo “get”. No estamos “extrayendo” nada del perro. No estamos extrayendo el nombre del perro. Le estamos pidiendo que nos diga su nombre. ¿Ven la diferencia?

Tampoco estamos hablando de semántica. Estamos diferenciando el estado mental procedimental del orientado a objetos. En la programación procedimental, trabajamos con datos, manipulándolos, asignándolos, extrayéndolos y borrándolos cuando es necesario. Nosotros estamos al mando, y los datos son un componente pasivo. El perro no significa nada para nosotros — es solo un “contenedor de datos”. No tiene una vida propia. Somos libres de tomar lo que sea necesario de él y asignar cualesquiera datos a él. Así es como C, COBOL, Pascal y muchos otros lenguajes procedimentales trabajan(ban).

Por el contrario, en un mundo verdaderamente orientado a objetos, tratamos a los objetos como organismos vivientes, con su propia fecha de nacimiento y un momento de muerte — con su propia identidad y hábitos, si lo desea. Podemos pedirle al perro que nos de una porción de datos (por ejemplo su peso), y él puede (o no) devolvernos la información. Pero siempre recuerde que el perro es un componente activo. Él decide que pasa después de que recibe nuestra petición.

Por este motivo es conceptualmente incorrecto tener métodos que empiezan con set o get en un objeto. Y no es un problema de romper el encapsulamiento, como mucha gente argumenta. Es acerca de si Ud. esta pensando en objetos o si aún está escribiendo COBOL con sintaxis de Java.

PS. Sí, Ud. se preguntará al respecto de, — ¿JavaBeans, JPA, JAXB, y muchos otras APIs de JAVA que dependen de la notación get/set? ¿Qué decir de la capacidad interna de Ruby que simplifica la creación de métodos de acceso? Bueno, esa es nuestra desgracia. Es mucho más fácil quedarse en el mundo primitivo de COBOL procedimental, que verdaderamente entender y apreciar el magnifico mundo de objetos verdaderos.

PPS. Se me olvido mencionar que, sí, la inyección de dependencias a través de setters es otro anti-patrón. Al respecto, en una entrada futura :)

PPPS. Esto es lo que yo sugiero para usar a cambio de getters: printers.

--

--