Wyobraź sobie, że potrzebujesz sposobu na sprawdzenie wartości obiektu i wykonanie innej akcji na tej podstawie. W programowaniu obiektowym sposobem na to jest użycie polimorfizmu, a jak to zrobić zobaczymy za chwilę.
Ale najpierw przyjrzyjmy się jak działa wyrażenie case
i jak możesz go użyć aby osiągnąć cel wspomniany powyżej.
Jak działa case?
Są dwie główne części wyrażenia case
. Klauzula case
oraz klauzula when
.
Klauzula when
jest wyrażeniem określającym odbiorcę operatora (domyślnie ===
), a klauzula case
określa przekazywany do niego argument.
Zobaczmy przykład.
case awhen String #..end
Możesz więc myśleć o powyższym przykładzie jako String === a
, lub String.===(a)
.
Na koniec, istnieje również klauzula else
, której można użyć, gdy nie ma dopasowania.
case my_objectwhen String "This is a string"else "I have no idea what this is"end
Operator potrójnego zrównania (==)
Domyślnie, case
używa operatora potrójnego zrównania (===
) do wykonania porównania.
Sytuacja z ===
jest taka, że nie ma on nic wspólnego z równością. Domyślnie jest to alias do operatora double equals (==
), który normalnie sprawdza, czy dwa obiekty mają równoważną wartość, ale można go zdefiniować tak, aby oznaczał cokolwiek.
Na przykład, zakres definiuje go jako alias dla includes?
, regex definiuje go jako alias dla match
, klasa dla is_a?
, proc dla call
. Rozumiemy to.
Ten operator działa zgodnie z oczekiwaniami z literałami, ale nie z klasami:
1 === 1 # => trueNumeric === Numeric # => false
To dlatego, że ===
jest aliasem dla kind_of?
. A oto docs dla kind_of?
.
kind_of?(class) → true or false
Zwraca true, jeśli class jest klasą obj, lub jeśli class jest jedną z nadklas obj lub modułów zawartych w obj.
To oznacza, że jeśli chcesz zrobić case ... when
nad klasą obiektu, to nie zadziała:
obj = 'Hi'case obj.classwhen String "It's a string"end
To dlatego, że, to tłumaczy się na String === String
. A to zwraca false
.
Operator domyślny
Wyrażenie case
posiada operator domyślny. A to oznacza, że nie musisz go określać. Jest to bardzo miłe, ponieważ możesz po prostu napisać when String
i domyślnie zostanie użyty operator ===
.
Ale są sytuacje, kiedy chcesz użyć innego operatora. I Ruby pozwala ci na to. Oto jak.
casewhen a < 3 "Smaller than 3"end
Case jest wyrażeniem
Tak jak mówi nazwa, cała ta funkcja jest wyrażeniem. Oznacza to, że oblicza się na wartość. Możesz więc przypisać wynik całego wyrażenia case
do zmiennej, jak poniżej.
value = case when a < 3 "Smaller than 3" end
Nie ma żadnego fall-through
Jeśli pochodzisz z innych języków, prawdopodobnie używałeś switch
statement lub czegoś podobnego, i zauważyłeś, że case
nie używa słów kluczowych, takich jak break
, aby przerwać przepływ.
To dlatego, że nie ma żadnego fall-through z case
. Po prostu zwraca wartość wyrażenia, które zostało dopasowane i to wszystko.
Wielokrotne dopasowania
Do tej pory używałeś tylko jednej wartości dla klauzuli when
. Ale możesz użyć wielu wartości.
case awhen 1..3 "Small number"end
Dopasowywanie regexów
Możesz użyć wyrażeń case
do dopasowania czegokolwiek. Ale na wszelki wypadek, gdyby to nie było oczywiste, możesz również dopasować regexy, lub lambdy. Oto przykład.
even = ->(x) { x % 2 == 0 }case awhen even "It's even"when /^+$/ "It's an integer"end
Zdefiniuj swoje własne
Jednym z mniej znanych faktów jest to, że możesz zdefiniować swoje własne komparatory.
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
W powyższym przykładzie, jeśli a
jest ciągiem o długości większej niż 100 znaków, to wyrażenie case
zwróci It's text
.
Użyj zamiast tego polimorfizmu
Nie będę się tutaj zagłębiał w zalety OOP, więcej na ten temat możesz przeczytać w artykule Programowanie obiektowe w Ruby, ale pokażę Ci jak możesz użyć polimorfizmu do zastąpienia wyrażenia case
.
Wersja 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
Wersja 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
Dobrym pomysłem jest usunięcie jak najwięcej kodu kontroli przepływu (i.e. if
s, oraz case
wyrażenia) jak to tylko możliwe. Poprawia to przestrzeganie zasady pojedynczej odpowiedzialności i ogólnie czyni program bardziej czytelnym.