Search code examples
smalltalksqueak

Refactoring a method in smalltalk


I'm a new user (actually studying it in a course) in Smalltalk (Squeak). I have a method to check if one rectangle equals to a given rectangle which looks like this:

isEqual:givenRec
    self a = givenRec a
    ifTrue: [
        self b = givenRec b
        ifTrue: [
            ^true
        ].
        ^false
    ].
    self b = givenRec a
    ifTrue: [
        self a = givenRec b
        ifTrue: [
            ^true
        ].
        ^false
    ].
    ^false

my question is - is there a way to better write this? for it to be more compact?

also - why cant I refer to a which is instanceVariableNames with self inside methods? thanks for the help!

EDIT:

this is how the class is defined:

MyShape subclass: #MyTriangle
    instanceVariableNames: 'a b c'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'Ex2'

MyShape simply derives from Object and has nothing.


Solution

  • You can make that more compact, yes.

    First, besides #ifTrue: there is also #ifFalse: and #ifTrue:ifFalse:, roughly having the effect of if-not--then and if--then--else.

    But also, we already have a logical AND condition, so why not use that:

    isEqual: givenRec
    
        (self a = givenRec a and: [self b = givenRec b])
            ifTrue: [^ true].
        (self b = givenRec a and: [self a = givenRec b])
            ifTrue: [^ true].
        ^false
    

    With #ifTrue:ifFalse:

    isEqual: givenRec
    
        (self a = givenRec a and: [self b = givenRec b])
            ifTrue: [^ true]
            ifFalse: [^ (self b = givenRec a and: [self a = givenRec b])]
    

    Also, we can do the return around the whole statement:

    isEqual: givenRec
    
        ^ (self a = givenRec a and: [self b = givenRec b])
            ifTrue: [true]
            ifFalse: [self b = givenRec a and: [self a = givenRec b]]
    

    But ifTrue: [true] is a bit redundant, let's use #or:

    isEqual: givenRec
    
        ^ (self a = givenRec a and: [self b = givenRec b]) or: 
          [self b = givenRec a and: [self a = givenRec b]]
    

    Nice and sweet, we also see the logical structure pretty easily. (note that I diverge from common formatting styles to point out the similarities and differences in the two logical expressions).

    We now have only one return ^, no #ifTrue:…


    For the instance variable question:

    When you define some instance variables in the class like you did, you can use them literally in your code to access them:

    Object subclass: #Contoso
        instanceVariableNames: 'things'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Examples'
    
    isThingPlusThreeSameAs: anObject
    
        ^ thing + 3 = anObject
    

    But typically, it is better to refer to instance variables via getters and setters, commonly called accessors. You have to write them manually or use the 'create inst var accessor' menu item of the second context menu (via “more…”) of a class in the browser:

    This will generate accessor methods of the form

    thing
    
        ^ thing
    
    thing: anObject
    
        thing := anObject
    

    and you can use them in other methods like this

    isThingPlusThreeSameAs: anObject
    
        ^ self thing + 3 = anObject