Mechanizm "switch-case" w Smalltalku
Język Smalltalk nie oferuje wraz ze swoimi standardowymi klasami odpowiednika mechanizmu switch-case, popularnego na przykład w językach wywodzących sie z języka C. Dlatego programiście, który chciałby takiego mechanizmu użyć pozostaje stworzenie go sobie samodzielnie. Poniżej został przedstawiony prosty sposób implementacji takiego mechanizmu.
Pierwszym krokiem będzie stworzenie klasy Switch jako podklasy klasy Object i zdefiniowanie trzech zmiennych: value, satisfied i response.
Object
subclass: #Switch
instVars: 'value satisfied response'
Zmienna value będzie przechowywała obiekt, dla którego wykonywany jest switch, zmienna satisfied informuje nas, czy któryś z warunków case został już spełniony, a zmienna response będzie przechowywała wynik operacji, która zostanie wykonana w wypadku spełnienia któregoś z warunków case. Następnie zdefiniujemy metodę klasową for: tworzącą instancję klasy Switch.
Switch>>for: anObject
^self new value: anObject
Aby uniknąć konieczności inicjalizacji zmiennej satisfied wartością false zdefiniujemy metodę isSatisfied, która zwróci true tylko w wypadku, gdy zmienna satisfied ma wartość true.
Switch>>isSatisfied
^satisfied == true
Teraz pozostaje nam stworzyć metody case:then: i default:.
Switch>>case: oneArgTestBlock then: execBlock
"The oneArgTestBlock must return a Boolean value when passed the value of the receiver."
self isSatisfied ifFalse:[
(oneArgTestBlock value: self value) ifTrue:[
self response: execBlock value.
self satisfied: true]].
Switch>>default: execBlock
self isSatisfied ifFalse: [self response: execBlock value].
^self response
I już możemy pisać:
(Switch for: x)
case: [:value | value = 1] then: ["..."];
case: [:value | value = 2] then: ["..."];
default: ["..."]
|
Dla tak prostego bloku testującego jak powyżej możemy stworzyć bardzo przydatną metodę w klasie Switch.
Switch>>caseIs: testObject then: execBlock
self case: [:value | testObject = value] then: execBlock.
Wtedy nasz poprzedni przykład będzie wyglądał następująco:
(Switch for: x)
caseIs: 1 then: ["..."];
caseIs: 2 then: ["..."];
default: ["..."]
|
Możemy też uprościć sobie przeszukiwanie kolekcji.
Switch>>caseIsAny: testCollection then: execBlock
self case: [:value | testCollection includes: value] then: execBlock.
Wszystko to ładnie wygląda, ale może ktoś chciałby to jeszcze bardziej uprościć i zamiast pisać Switch for: aValue móc po prostu napisać aValue switch. Nic prostszego. W tym celu wystarczy dodać do klasy Object metodę switch.
Object>>switch
^Switch for: self
I już można nasz poprzedni przykład zapisać następująco:
x switch
caseIs: 1 then: ["..."];
caseIs: 2 then: ["..."];
default: ["..."]
|
I to już nawet przypomina składnię switch-case z innych języków programowania. :-)
(pn)
Przedstawioną wyżej metodę switch można też zdefiniować w następujący sposób:
Object>>switch
^Switch for: self value
Możliwa jest wtedy również następująca składnia:
[x*10] switch
caseIs: 1 then: ["..."];
caseIs: 2 then: ["..."];
default: ["..."]
|
Powyższa implementacja metody umożliwia również proste wykorzystanie instancji klasy ValueHolder w strukturze switch-case. Metoda switch może teraz zostać wysłana bezpośrednio do takiego obiektu, odbiorcą metod typu case będzie obiekt przechowywany przez instancję klasy ValueHolder.
W przedstawionym powyżej mechaniźmie switch-case można również zrezygnować z wykorzystania zmiennej satisfied. Możliwe będzie wtedy wykonanie kilku bloków spełniających warunek case, tak jak ma to miejsce w języku C.
(wh)
|