Imagine que precisa de uma forma de verificar o valor de um objecto, e executar uma acção diferente com base nisso. A forma de Programação Orientada para Objectos é utilizar polimorfismo, e veremos como fazê-lo num segundo.
Mas primeiro, vejamos como funciona a expressão case
, e como pode utilizá-la para atingir o objectivo acima mencionado.
Como funciona o caso?
Existem duas partes principais da expressão case
. A cláusula case
, e a cláusula when
.
A cláusula when
é a expressão que define o receptor do operador (por defeito para ===
), e a cláusula case
define o argumento que lhe foi transmitido.
Vejamos um exemplo.
case awhen String #..end
Por isso, pode-se pensar no exemplo acima como String === a
, ou .
Finalmente, há também uma cláusula else
que pode utilizar quando não houver correspondência.
case my_objectwhen String "This is a string"else "I have no idea what this is"end
O operador triplo é igual a (====)
Por defeito, case
utiliza o operador triplo é igual a (===
) para fazer a comparação.
A coisa com ===
é que não tem nada a ver com igualdade. Por defeito, é aliado ao duplo operador igual (==
) que normalmente verifica se dois objectos têm valor equivalente, mas pode ser definido para significar qualquer coisa.
Por exemplo, um intervalo define-o como um alias para includes?
, um regex define-o como um alias para match
, uma classe para is_a?
, um proc para call
. Fica-se com a ideia.
que o operador funciona como esperado com literals, mas não com classes:
1 === 1 # => trueNumeric === Numeric # => false
Isso porque o ===
é um pseudónimo para kind_of?
. E aqui estão os documentos para kind_of?
.
kind_of?(class) → true or false
Retorna true se a classe for a classe de obj, ou se a classe for uma das superclasses de obj ou módulos incluídos no obj.
Isto significa que se quiser fazer um case ... when
sobre a classe de um objecto, isto não funcionará:
obj = 'Hi'case obj.classwhen String "It's a string"end
Isso porque, traduz-se por String === String
. E isso retorna false
.
O operador por defeito
O case
expressão tem um operador por defeito. E isso significa que não tem de o especificar. Isso é muito bom, porque pode simplesmente escrever when String
e por defeito o ===
operador.
mas, há alturas em que quer utilizar um operador diferente. E a Ruby permite-lhe fazê-lo. Eis como.
casewhen a < 3 "Smaller than 3"end
Caso é uma expressão
Apenas como o nome diz, a coisa toda é uma expressão. Isto significa que se avalia a um valor. Assim, pode atribuir o resultado de toda a expressão case
a uma variável, como assim.
value = case when a < 3 "Smaller than 3" end
Não há nenhuma quebra
Se vem de outras línguas, provavelmente usou uma declaração switch
ou algo semelhante, e notou que case
não usa palavras-chave como break
para quebrar o fluxo.
Isso é porque não há queda com case
. Apenas devolve o valor da expressão que foi correspondida e pronto.
Múltiplas correspondências
Up até agora, só se utilizou um valor para a cláusula when
. Mas pode usar múltiplos valores.
case awhen 1..3 "Small number"end
Matching regexes
P>Pode usar case
expressões para corresponder a qualquer coisa. Mas no caso de não ser óbvio, pode também combinar regexes, ou lambdas. Eis um exemplo.
even = ->(x) { x % 2 == 0 }case awhen even "It's even"when /^+$/ "It's an integer"end
Definir os seus próprios
Uma dos factos menos conhecidos é que pode definir os seus próprios 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
No exemplo acima, se a
for uma sequência de mais de 100 caracteres, então a expressão case
retornará It's text
.
Utilizar polimorfismo em vez disso
Não vou entrar nos benefícios do OOP aqui, pode ler mais sobre isso no artigo Programação Orientada para Objectos com Ruby, mas vou mostrar-lhe como pode usar o polimorfismo para substituir uma expressão case
.
A versão 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
A versão 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
É uma boa ideia apagar tanto código de controlo de fluxo (i.e. if
s, e case
expressões) o mais possível. Melhora a adesão ao Princípio da Responsabilidade Única e torna o programa mais legível em geral.