Search code examples
code-generationsmalltalkgetter-setterpharo

How can getter/setter code be generated automatically for a class in Pharo or Squeak?


I have a long list of instance variables to create for a class that I want to generate the code for, rather than do it by hand. The list comes from an existing SQL database. My intention is to do it all in a pure object-oriented way with Smalltalk first, and as I learn more, save the data back to the database and work from it directly.

Is there a way of passing the list of names to method that will generate them and add them to the class definition?

In fact is there a way of adding or modifying class definitions dynamically in Smalltalk? I suspect there must and I would like to know a best practices approach.

Update: What I have in mind is more like passing a list of the instance variables to a method that will create them automatically.

It is more like:

addVariablesAndAccessors className: MyClass variablesList: ('aaaa', 'bbbb', 'cccc')

which will then result in a call to

AddVariables className: MyClass variableList: ('aaaa' 'bbbb' cccc')

and

generateAccessors className: MyClass variableList: ('aaaa' 'bbbb' cccc')


Solution

  • In Squeak, you have Behavior>>addInstVarName: aString, so for instance, you could do something like:

    String addInstVarName: 'foo' 
    

    Squeak also has refactoring support to generate accessors automatically. You can either use it directly or have a look at AbstractInstanceVariableRefactoring>>createAccessors to get some inspiration on how to implement your own ;-)

    Another quite hacky but not so uncommon solution would be to just generate the instance variables, but instead of adding accessors, you overwrite doesNotUnderstand:, which gets called when an undefined selector is sent to your objects. There, you could check if you have an instance variable named according to the message, and return / change it if it is the case. Otherwise you just do super doesNotUnderstand: aMessage.


    Regarding your comment: Classes are objects, too, so you don't have to do anything special to use them as parameters. On which class you add it is totally up to you and doesn't really matter. So a method to add instance variables could look like this:

    addVariablesNamed: aCollection on: aClass
    
        aCollection do: [:each | aClass addInstVarName: each]
    

    and you could call it like this:

    yourObject addVariablesNamed: #('foo' 'bar' 'baz') on: ClassX
    

    You can find examples on how to generate accessor methods in the class CreateAccessorsForVariableRefactoring