Juan Aguerre
Juan Aguerre

Reputation: 388

Refactoring if-chains in Smalltalk without class explosion

As Smalltalk discourages the use of caseOf:, what aternatives exists to implement the following situation without class explosion?

self condition1
        ifTrue: [ self actionForCondition1 ]
        ifFalse: [ 
            self condition2
                ifTrue: [ self actionForCondition2 ]
                ifFalse: [ 
                    self condition3
                        ifTrue: [ self actionForCondition3 ]
                        ifFalse: [ ....  ] ] ]

Upvotes: 7

Views: 2913

Answers (4)

Dale Henrichs
Dale Henrichs

Reputation: 1293

You should take a look at double dispatching. Depending upon how the tests and actions are implemented you may be able to use double dispatching to great advantage.

You want to end up with code that looks like the following:

self conditionAction performAction.

or

self conditinAction performAction: actionArgs

The method #conditionAction would have to return a unique instance of an object for each unique condition (without using case statements itself:).

You wouldn't want to force the issue by creating classes just to avoid "case statements", but in real world you may already have some unique classes that you can leverage.

Upvotes: 1

Niall Ross
Niall Ross

Reputation: 76

If you scroll to near the end of

http://www.desk.org:8080/CampSmalltalk/control%20flow

you will find the sentence

"There are four ways to express a case-statement in Smalltalk."

followed by links to examples.

The text is in the middle of a slightly longer series of pages and makes occasional references to hypothetical tutor and students ina Smalltalk course for purposes of illustration; you can ignore that for the purposes of your question)

Upvotes: 6

Igor Stasenko
Igor Stasenko

Reputation: 1137

I think, at the moment when you have to write this code, i would ask myself, why i have to write so many conditions in order to proceed further with a single step in my algorithm. Maybe it is time to think what is wrong with model? Especially, if you consider that a message lookup semantics is actually a case statement:

selector = selector1 ifTrue: [ invoke method1 ] 
  ifFalse: [ selector= selector2 ifTrue: [ invoke method2 ] 
   ifFalse: [...] ]]].

So you should try to turn this into your advantage - use VM's switch statement instead of writing own.

By applying a simple principle: do not ask (object isSomething ifTrue:[ self doSomething ]) but tell (object doSomething), you can avoid having many branches in the code. Of course, sometimes it is not applicable and heavily depending on situation, but i often prefer to have extra message dispatch, rather than another branch in code.

Upvotes: 2

Lukas Renggli
Lukas Renggli

Reputation: 8947

Depends on how your conditions exactly look like?

  • If your conditions are type tests

    anObject isKindOf: aClass
    

    you can dispatch on the class instead, that means you call an action method on anObject.

  • If your conditions are equality tests

    anObject = anotherObject
    

    you can use a dictionary with the object as key and the action as a block closure.

  • As a last restort and if nothing else helps you might want to rewrite the proposed code to avoid unnecessary nesting:

    self condition1
        ifTrue: [ ^ self actionForCondition1 ].
    self condition2
        ifTrue: [ ^ self actionForCondition2 ].
    self condition3
        ifTrue: [ ^ self actionForCondition3 ].
    ...
    

Upvotes: 8

Related Questions