Hace unos días me encontraba en una sesión de review de código en la oficina de un cliente y me topé con el típico problema recurrente del cual creo que ya muchos estamos empapados: “La clase superhéroe”, si, esa amiga que no sólo se conecta a la base de datos, sino que también crea la consulta, chequea los datos, genera el id aleatorio del usuario y también al mismo tiempo inserta y cambia los registros en la base de datos, todo en una “compacta” clase que hace literalmente todo.
No, este post no es acerca de SRP (Single Responsability Principle) o de principios de diseño y pensamiento en objetos ni tampoco se trata de la vieja escuela de “modularización” usando subrutinas (me imagino que alguien recuerda sus viejas lecturas de hace muchos años cuando aprendían algo como Pascal o BASIC). En vez de eso, decidí conversar un poco de lo que he aprendido mientras continuo mi jornada de aprendizaje en lenguajes funcionales (usando Haskell).
Advertencia: Estoy en el proceso de aprendizaje de lenguajes funcionales y Haskell, es muy probable que lo que exprese o diga en este post realmente sean producto de mi alta taza de ignorancia al respecto.
En Haskell las funciones son ciudadanos de primer orden, son uno más, de hecho, son sumamente importantes (me imagino que en los demás lenguajes funcionales el principio es el mismo). El principio que rige o elimina la duplicidad y la reutilización de operaciones en lenguajes funcionales es la simple composición de funciones, o sea, de forma similar a como deberíamos desarrollar aplicaciones mediante composición de objetos, en un lenguaje funcional, funciones complejas se crean mediante funciones más simples, primitivas y sencillas. Lección a aprender, procura que tu “método” o “función” (o como quieras llamarlo, si quieres, hasta “subrutina” le puedes poner) haga una y sólo una cosa.
Tomemos el siguiente ejemplo (tomado del libro Programming in Haskell de Graham Hutton) dónde definimos un cifrador de cesar usando simples funciones en Haskell (probablemente se puede definir en una forma mucho más óptima, pero que se yo!)
import Data.Char
{-
- Caesar cypher definition
-}
-- Transform a char to its numeric representation
let2int :: Char -> Int
let2int a = ord a - ord 'a'
-- Transform an integer to a char representation
int2let :: Int -> Char
int2let a = chr (ord 'a' + a)
-- Shift a character by its factor
shift :: Int -> Char -> Char
shift n a | isLower a = int2let z
| otherwise = a
where
x = let2int a
y = x + n
z = y `mod` 26 -- because we can reach z someday!
-- Now it is time to encrypt!
encode :: Int -> String -> String
encode n xs = [shift n x | x <- xs]
La forma en que funciona es simple, al final esta declarada la función encode que utiliza a su vez list comprehension (hablaré de esto después) y a la función definida shif, que a su vez utiliza la función let2int e int2let que también usan a la función incluída ord (que existe en el space Data.Char).
Quizás todo esto se pueda ver “trivial” pero es muy común ver métodos o “Modules” con métodos en Visual Basic que literalmente hacen todo: piden la entrada a la pantalla al usuario, verifican que la entrada sea la correcta, luego hacen el calculo y por último le muestran al usuario el resultado, si, todo en el mismo Main del módulo…
Creo que debemos comenzar a aprender un poco más de lenguajes simples como los lenguajes funcionales, sin construcciones exóticas, sin extensiones mágicas… simples composiciones, simple reutilización, donde la simpleza es el motivo. Claro, esto no implica que no podemos hacer cosas “complejas” en un lenguaje funcional, por ejemplo, Darcs (un sistema de control de versiones distribuido) y Leksah (un IDE para Haskell) se encuentran ambos hechos en Haskell.
La próxima vez que se topen con un super gordo método main, recuerden que es mejor ponerlo a dieta, aprendan de Haskell
¡Saludos!
NOTA: mi amigo José Romaniello me comenta que el patrón o el “antipatrón” de la clase superhéroe se conoce como “God Class”, aunque yo prefiero el nombre de superhéroe

