Imagina que necesitas una forma de comprobar el valor de un objeto, y realizar una acción diferente en función de ello. La manera de la Programación Orientada a Objetos es usar el polimorfismo, y veremos cómo hacerlo en un segundo.
Pero primero, veamos cómo funciona la expresión case
, y cómo puedes usarla para lograr el objetivo mencionado anteriormente.
¿Cómo funciona el caso?
Hay dos partes principales en la expresión case
. La cláusula case
y la cláusula when
.
La cláusula when
es la expresión que define el receptor del operador (por defecto ===
), y la cláusula case
define el argumento que se le pasa.
Veamos un ejemplo.
case awhen String #..end
Así que puedes pensar en el ejemplo anterior como String === a
, o String.===(a)
.
Por último, también hay una cláusula else
que puedes utilizar para cuando no hay ninguna coincidencia.
case my_objectwhen String "This is a string"else "I have no idea what this is"end
El operador triple igual (===)
Por defecto, case
utiliza el operador triple igual (===
) para hacer la comparación.
Lo que ocurre con ===
es que no tiene nada que ver con la igualdad. Por defecto, está aliasado al operador doble de igualdad (==
) que normalmente comprueba si dos objetos tienen un valor equivalente, pero se puede definir para que signifique cualquier cosa.
Por ejemplo, un rango lo define como un alias para includes?
, un regex lo define como un alias para match
, una clase para is_a?
, un proc para call
. Ya te haces una idea.
Ese operador funciona como se espera con los literales, pero no con las clases:
1 === 1 # => trueNumeric === Numeric # => false
Eso es porque el ===
es un alias de kind_of?
. Y aquí están los docs para kind_of?
.
kind_of?(class) → true o false
Devuelve true si class es la clase de obj, o si class es una de las superclases de obj o de los módulos incluidos en obj.
Esto significa que si quieres hacer un case ... when
sobre la clase de un objeto, esto no funcionará:
obj = 'Hi'case obj.classwhen String "It's a string"end
Esto es porque, se traduce en String === String
. Y eso devuelve false
.
El operador por defecto
La expresión case
tiene un operador por defecto. Y eso significa que no tienes que especificarlo. Eso está muy bien, porque puedes simplemente escribir when String
y por defecto aparece el operador ===
.
Pero, hay veces que quieres usar un operador diferente. Y Ruby te permite hacerlo. Aquí te decimos cómo.
casewhen a < 3 "Smaller than 3"end
Case es una expresión
Tal como dice el nombre, todo es una expresión. Es decir, se evalúa a un valor. Así que puedes asignar el resultado de toda la expresión case
a una variable, así.
value = case when a < 3 "Smaller than 3" end
No hay caída
Si vienes de otros lenguajes, probablemente hayas usado una sentencia switch
o algo similar, y habrás notado que case
no utiliza palabras clave como break
para romper el flujo.
Esto se debe a que no hay caída con case
. Sólo devuelve el valor de la expresión con la que se ha encontrado una coincidencia y ya está.
Múltiples coincidencias
Hasta ahora, sólo has utilizado un valor para la cláusula when
. Pero puedes utilizar múltiples valores.
case awhen 1..3 "Small number"end
Combinación de expresiones regulares
Puedes utilizar expresiones case
para que coincida cualquier cosa. Pero por si acaso no es obvio, también puedes hacer coincidir regexes, o lambdas. Aquí tienes un ejemplo.
even = ->(x) { x % 2 == 0 }case awhen even "It's even"when /^+$/ "It's an integer"end
Define los tuyos
Uno de los hechos menos conocidos es que puedes definir tus propios comparadores.
Text = Struct.new(:min_length) do def ===(string) string.size > min_length && string.is_a?(String) endendcase awhen Text.new(100) "It's text"end
En el ejemplo anterior, si a
es una cadena de más de 100 caracteres, entonces la expresión case
devolverá It's text
.
Usa el polimorfismo en su lugar
No voy a entrar aquí en las ventajas de la POO, puedes leer más sobre eso en el artículo Programación Orientada a Objetos con Ruby, pero sí voy a mostrarte cómo puedes usar el polimorfismo para sustituir una expresión case
.
La versión case
class Person attr_reader :country def initialize(country) @country = country end def nationality case country when "USA" "This guy is an American" when "Romania" "This guy is a Romanian" end endendjohn = Person.new("USA")puts john.nationality # => This guy is an American
La versión OOP
class Person attr_reader :country def initialize(country) @country = country end def nationality country.nationality endendclass America def nationality "This guy is an American" endendclass Romania def nationality "This guy is a Romanian" endendjohn = Person.new(America.new)puts john.nationality # => This guy is an American
Es una buena idea eliminar todo el código de control de flujo (i.e. if
s, y case
expresiones) como sea posible. Mejora la adhesión al principio de responsabilidad única y hace que el programa sea más legible en general.