Ir al contenido principal

Getters y Setters grandes enemigos de la OOP

Es mucho el artículo, publicación y blogs de , ¿supuestos?, entendidos en el tema que hablan sobre, o exponen el problema con, el uso o más bien ABUSO de los Getters y Setters en la programación orientada a objetos (OOP).

Es este un tema recurrente y para nada nuevo, no señor, de hecho viene dando vueltas en el entorno de desarrollos OOP desde hace un buen rato. Pero....

¿Cuál es el gran problema con usar getters y setters?


Interesante pregunta...

En principio no parece haber nada malo con ellos, los getters y setters, y muchas herramientas y frameworks promueven su uso, es más, existen cientos y miles de programas construidos con estos elementos y funcionan bastante bien.

Sí funcionan ¿porqué cambiarlos?

¿Que hace que cada cierto tiempo salga algún papanatas con ínfulas de sabio, como el autor, diciendo que el uso de estos recursos de programación debe ser evitado, abolido e incluso castigado!!!?

Algo debe estar sucediendo para que se pretenda imponer reglas que eliminan de facto a estas formas de construcción.

Intentaré responder a estas cuestiones de la mejor manera.

Vamos con un ejemplo...

Me gusta ver televisión a un volumen que se podría considerar alto, al menos para mi señora lo es, por lo cual frecuentemente me encuentro con la tarea de tener que ajustar el nivel del audio al volumen deseado. Esto último lo realizo o bien pulsando un botón o moviendo una perilla hasta que consigo el resultado esperado.

Así de simple. Pulso un botón dispuesto para tal fin, o muevo un control con igual propósito. Recibo por lo general un feedback del nivel de volumen alcanzado tanto en forma numérica como el que llega a mis oídos.  Y ya.

No soy técnico en electrónica, ni tengo que serlo para operar un control remoto de un televisor u otro aparato cualquiera.

Se imagina que para lograr usar el control remoto y subir o bajar el volumen de la Tv tuviera que hacer algo como lo siguiente:

  • Obtener tarjeta de circuito electrónico.
  • Buscar chip de volumen.
  • Buscar condensador de potencia.
  • Almacenar carga.
  • Poner carga en circuito... 

y listo... volumen modificado!!!

¿Capta la diferencia?

En el primer caso usamos el control remoto mediante el "Contrato" o "Interface" que el mismo nos expone para poder operar sobre el. Sin preocuparnos de cómo está construido ni cual es su estructura interna.

En el segundo caso debemos ser consientes de cómo demonios está construido el control remoto, es más, debemos saber como funciona internamente, para poder lograr  modificar el volumen del Tv.

Ambos logran el mismo objetivo. Intrínsecamente y técnicamente hablando ninguna forma es incorrecta. El problema es que una de esas formas es totalmente insegura e inmantenible mientras que la otra es sencilla y protegida. Ud decide cual es cual.

Y es esta última forma de trabajo la que promueven y permiten los getters y setters. Por eso son tan dañinos. Así de simple. Permiten hacer una Vivisección del objeto al que pertenecen, descubriendo sus componentes y estructuras internas y facilitando su modificación, alteración y reemplazo, lo cual no siempre resulta deseable, de hecho pocas veces lo es, y en general termina siendo una practica contraproducente.

En vez de programar para una Abstracción, Contrato o Interface, los programadores actuales terminan PIDIENDO un montón de datos o estructuras a los objetos para ellos, los programadores, hacer el trabajo y modificar o actualizar  el estado interno del objeto. Trabajan exactamente como se describe en el segundo ejemplo.

Uno de estos supuestos desarrolladores orientados a objetos hacen un diseño o construcción como la siguiente:

    public classs ControlRemoto{
        private Integer volumen;
        public Integer getVolumen(){
            return volumen;
        }
        public void setVolumen(Integer volumenNuevo){
            volumen= volumenNuevo;
        }
    }

    public class VolumenUtil{
        public static void subirVolumen(ControlRemoto control){
            control.setVolumen(control.getVolumen() + 1);
        }
        public static void bajarVolumen(ControlRemoto control){
            control.setVolumen(control.getVolumen() - 1);
        }
    }

    public class Tester{
        public static void main(String[] args){
            ControlRemoto myControl = new ControlRemoto();
            myControl.setVolumen(40);
            System.out.println("Volumen actual : " + myControl.getVolumen());
            VolumenUtil.subirVolumen(myControl);
            VolumenUtil.subirVolumen(myControl);
            VolumenUtil.bajarVolumen(myControl);
            System.out.println("Volumen  final : " + myControl.getVolumen());
        }
    }

Admítalo, Ud ha visto, y seguramente hasta ha escrito, código como ese. Usando clases "utilitarias" y pidiéndole datos al objeto para Ud hacer el trabajo. Para Ud estar bajo control.

¿Ve como se expone la construcción interna del objeto?
¿Ve como desde afuera se puede cambiar el estado del objeto ControlRemoto desde cualquier parte del sistema?

Esto implica que, en un ambiente multihilos y distribuido, nunca podríamos confiar en los objetos ControlRemoto, por que cualquiera, desde cualquier parte del programa, podría cambiar su estado en forma arbitraria, sin que los objetos ControlRemoto tengan control ni conocimiento sobre ello, y eso es FATAL. Es BASURA. Eso es peor que la programación procedimental.

Digamos ahora que Ud Cree haber entendido el problema y propone, inocentemente, el código siguiente como alternativa o mejor solución al anterior:



    public classs ControlRemoto{
        private Integer volumen;

        public ControlRemoto(Integer volumen){
            this.volumen = volumen;
        }
        public Integer subirVolumen(){
            return ++volumen;
        }
        public Integer bajarVolumen(){
            return --volumen;
        }
        public Integer getVolumen(){
            return volumen;
        }
    }

    public class Tester{
        public static void main(String[] args){
            ControlRemoto myControl = new ControlRemoto(40);
            System.out.println("Volumen actual : " + myControl.getVolumen());
            myControl.subirVolumen();
            myControl.subirVolumen();
            myControl.bajarVolumen();
            System.out.println("Volumen  final : " + myControl.getVolumen());
        }
    }

Y esta ya es una gran mejora en verdad sobre la propuesta anterior. No obstante, imaginemos que pasaría si en la clase Tester hiciéramos  algo como lo siguiente:

    Integer vol = myControl.getVolumen();
    vol += 40; 
    System.out.println("Volumen actual : " + myControl.getVolumen());

¿Cuál sería el resultado impreso? ¿Se alteró el valor de la propiedad "volumen"?
De ser así, ¿Cómo evitamos este efecto no deseado?

Es por ello que los objetos deben ser lo mayormente inmutables que sea posible.
Internamente necesito que la propiedad volumen pueda ser modificada. Pero es mi deber garantizar que desde afuera de la clase no puedan cambiarla o alterarla.

Propongamos entonces una nueva versión de la clase CotrolRemoto:

    public classs ControlRemoto{
        private Integer volumen;

        public ControlRemoto(Integer volumen){
            this.volumen = volumen;
        }
        public Integer subirVolumen(){
            return ++volumen.clone();
        }
        public Integer bajarVolumen(){
            return --volumen.clone();
        }
        public Integer getVolumen(){
            return volumen.clone();
        }
    }

    public class Tester{
        public static void main(String[] args){
            ControlRemoto myControl = new ControlRemoto(40);
            System.out.println("Volumen actual : " + myControl.getVolumen());
            myControl.subirVolumen();
            myControl.subirVolumen();
            myControl.bajarVolumen();
            Integer vol= myControl.getVolumen();
            vol+= 50;
            System.out.println("Volumen  final : " + myControl.getVolumen());
        }
    }

Las pruebas se las dejo en manos de Uds.

Los getter y setter, su uso y abuso, son los que han provocado comentarios como el de quien dijo que:

"Con la programación orientada a objetos se ha conseguido es un desastre, queríamos una Banana, pero lo que obtuvimos fue un Gorila sosteniendo una Banana y la Jungla ENTERA!!!".

Recuerde, dígale al objeto QUE hacer y dele los elementos para hacerlo. NO le pida datos al objeto para Ud hacer el trabajo. Sea flojo, mandon e inteligente al mismo tiempo. Deje que los objetos trabajen para Ud.

Espero sus comentarios y opiniones.

Comentarios

Publicar un comentario

Entradas populares de este blog

El Melange todavía corre

Ese era el estribillo de un capítulo de unas de mis series favoritas de la infancia, Meteoro o Speed Racer. En ese capítulo un auto “fantasma” el X-3, aparecía de imprevisto y dejaba a todos asombrados con su rendimiento y prestaciones y volvía a desaparecer. Traigo ese episodio a colación puesto que recientemente sostuve una amena charla con un querido amigo, en la que el me manifestaba como los Mainframes habían muerto, o mejor dicho, el concepto de la computación distribuida basada en Mainframes había desaparecido. Para variar, yo no estuve de acuerdo, y le dije que por el contrario, el modelo de computación basado en Mainframes está mas vigente que nunca. Estos fueron mis argumentos:

Primeros pasos con Camunda BPM – Modelando un Proceso BPMN 2.0

Tenemos entre manos la tercera publicación de nuestra serie sobre la Plataforma de BPM de Camunda .  El día de hoy vamos, por fin, a empezar a modelar o construir nuestro primer proceso sencillo en notación BPMN 2.0. Para ello vamos a usar el modelador o editor que ya hemos instalado en nuestra primera publicación , y vamos a guardarlo en la sección de recursos del proyecto Maven Java que configuramos en la segunda publicación . Así que, como ya es costumbre, manos a las sobras…

Como configurar jBPM para usar nuestra propia Base de Datos en un sólo paso

Llevo un buen rato trabajando con jBPM en su serie 6.x, y mi opinión sobre este producto en la versión mecionada no ha mejorado para nada. Es una herramienta plena de funciones y caracteristicas avanzadas, pero tambien está llena de Bugs y es realmente inestable, sobre todo en el ambiente de modelamiento.  Así mismo, debo decir que tiene una muy aceptable API REST y que el motor de procesos y la consecuente ejecución de los procesos es estable y bastante rápida. En esta publicación daré inicio a una serie de artículos que hablan sobre ciertas configuraciones comunes e importantes que se hacen con jBPM. Hoy iniciamos con la configuración de jBPM para que use nuestra base de datos favorita. Esto tiene sentido porque el producto viene con la base de datos H2 por omisión, la cual es excelente para pruebas y evaluaciones rápidas de la herramienta, pero es completamente inaceptable en un ambiente de desarrollo, QA o producción cualquiera. Así que manos a l