Porque la OOP es prima de Cthulhu

Hoy vengo con ganas de pelea. Quienes me conocen saben que llevo muchos años programando y casi siempre proyectos con modelos de datos bastante grandes. El ejemplo por excelencia de estos años de trabajo es mmmelon: una plataforma para colaboración entre equipos con un modelo bastante complejo y además desarrollado sobre Meteor, una plataforma en pleno proceso de maduración. La conclusión y argumento central de este post es sencilla: ¡Basta ya de modelos orientados a objetos!

Antes de continuar, me gustaría aclarar que no me considero un experto, solo voy a contar mi experiencia personal y porqué creo que he tomado el camino correcto.

Mi experiencia con mmmelon ha sido en javascript pero creo que esta misma idea se puede llevar a casi cualquier lenguaje.

En realidad el modelo OOP encaja a la perfección con un modelo de datos. A base de propiedades y métodos describimos una entidad de nuestro modelo, al igual que describíamos, cuando estábamos aprendiendo, un perro que heredaba de la clase animal.

En el caso de javascript hasta el estándar ES6 no han aparecido las clases como tal.

Por mi parte, he pasado por las distintas fases según el punto del desarrollo en el que me encontraba, experimentando distintas opciones para ver cual encajaba mejor con mi flujo de trabajo en Meteor.

El primer paso fue una chapuza: aprendiendo con Meteor, todo mezclado, algo bastante lamentable pero era una primera fase de experimentación y contacto con Meteor, no duro demasiado.

El siguiente paso fue crear un modelo orientado a objetos utilizando las opciones que daba ES5. El resultado parecía bastante mejor, pero al empezar a trastear con ES6 vi que se podía mejorar: podía reducir el código utilizando las nuevas class de ES6.

Ese fue el tercer paso, convertir mi modelo orientado a objetos con código ES5 a ES6. Con ello me pareció que tenía una forma seria y limpia de programar todo el modelo. El problema vino precisamente en el siguiente paso, el de empezar a programar un modelo en serio: se había terminado el trastear y curiosear, ya tenía en mente como iba a hacerlo y llegaba el momento de empezar a picar tests (pero no hablaré de los problemas derivados de hacer tests en Meteor).

Los tests en sí no eran el problema, pero evidenciaron algo que yo no había visto hasta ese momento, pese a leer bastante sobre programación funcional y ver casos de gente tropezando con las mismas piedras desde hace décadas. Parece que hasta que uno no tropieza no ve el problema.

Estos problemas evidenciados por los tests eran 2 principalmente:

Contexto

Parece obvio pero es algo completamente extendido y usado en millones de aplicaciones. La noción de métodos que mutan un objeto y funciones que dependen del contexto en el que están es una mala idea.

Para ver que funciones estaban pecando de este error empecé a buscar todas las que usaban this. No es una regla absoluta, pero si aparece this en una función podemos sospechar que algo no va bien. Las funciones deben tener unos parámetros de entrada y un valor de retorno. Leer o escribir en el contexto es el demonio, Cthulhu o el primigenio que más miedo instigue en tu mente de insignificante mortal.

Mutabilidad

La razón por la que intento evitar la mutabilidad es similar: si una función modifica los parámetros de entrada rompe por completo la idea anterior. Todo el efecto que debe tener una función es su valor de retorno.

Estas ideas no son mías, si yo he llegado a aplicarlas es porque personas mucho más inteligentes que yo han pensado y desarrollado de verdad estos conceptos y escrito decenas de páginas sobre ello. La programación funcional trata muchos de estos temas y aunque parezca algo nuevo o moderno tiene más años que la tos.

Gracias a esta experiencia decidí crear una especificación común a todos los modelos que desarrollo bajo Meteor. Incluye muchas cosas únicas de Meteor pero estas dos ideas se reflejan en toda la especificación.

El resultado de aplicar estas ideas me lleva a transformar:

class Task{

complete(){
this.completed = true;
this.save();
}
}

en:

Task = {};
Task.complete = function(taskId){
Tasks.update(…)
}

Con este post espero generar algo de discusión: no creo que una decisión en programación sea definitiva, si no que es imprescindible mantenerse abierto a que alguien más ingenioso o con más experiencia te muestre que se puede hacer mejor, y estaría encantado de tener que reconocer que esta no es la mejor forma, porque significaría que existe alguna más eficaz.

Show your support

Clapping shows how much you appreciated David’s story.