Chapter 19 - Creating Classes
In object oriented programming classes serve as the blueprint for objects, the genotype information that results in instances of phenotypes. Like a recipe for cookes. This can be extremely useful when creating data structures that have properties (parameters or variables) and have behaviours (methods or functions).
For further information on what a class is, then a good start is the Wikipedia:
// run this line in SuperCollider “open ‘http://en.wikipedia.org/wiki/Class_(computer_science)’“.unixCmd;
Creating Classes
A good introduction to writing classes are in the Help documentation
“Writing-Classes”.openHelpFile
So below we have two TestClasses, one that subclasses the first one (like the guitar is a subclass of a string instrument).
Save both classes in a document that could be called “TestClass.sc� and make sure it is saved in the classpath of SuperCollider (where it is compiled on recompilation or startup). On the Mac the classpath for third party classes is in ~Library/Application Support/SuperCollider/External.
TestClass {
classvar <>myvar; // classvariables
var <>addnr, >addnrSet, <addnrGet; // instance variables
// this is a normal constructor method
*new { arg argaddnr;
^super.new.initTest(argaddnr)
}
initTest { arg argaddnr;
addnr = argaddnr ? 3;
// do initiation here
}
calc {arg a, b;
var c;
c = a+b;
^c // return
}
}
TestClass2 : TestClass {
calc { arg a, b;
var c;
c = a * b + addnr;
^c;
}
setAddNr_ { arg newnr;
addnr = newnr;
}
getAddNr {
^addnr;
}
}
When the classes have been saved and compiled we can now test the class:
t = TestClass.new //
t.calc(3,4)
r = TestClass.new(9)
r.addnr
v = TestClass2.new
v.calc(3,4)
v.addnr_(55)
v.addnr // our new class
t.addnr // the other of course still is just 9
v.addnrSet = 33 // we can set this number (because of > (a setter) )
v.addnrSet_(33) // another way of setting a variable (same as = )
v.addnrGet = 33 // Wrong! we cannot set this number ( because it is a getter < )
// proper object orientated programming uses setter and getter methods
// (rather than accessing variables directly)
// here we use the setAddNr_ method to set our variable.
v.setAddNr_(333)
// and we can look at it:
v.addnr
// but should really look at it with the getter method we made:
v.getAddNr
You can see that the < > symbols in the class are so called setters (>) and getters (<), which, if specified in front of the property specification, can serve instead of methods that set the properties. Therefore you can equally write
v.addnr(44) // using a setter and v.addnrSet(44) // using a method
Another test class
SonicArtsClass {
var win, textfield, textfield2, rect; // get text but set text2
var name, <>profession; // a getter and setter variable
var friends;
*new { arg name, rect, color;
^super.new.initSAClass(name, rect, color);
}
initSAClass { arg argname, argrect, color;
var scramblebutton;
rect = argrect;
name = argname;
win = SCWindow(name, rect, resizable:false).front;
win.view.background_(color);
textfield = SCStaticText(win, Rect(10, (rect.height/2)-30, rect.width, 30));
textfield.string_("");
textfield.font_(Font("Helvetica-Bold", 24));
textfield2 = SCStaticText(win, Rect(10, (rect.height/2)+30, rect.width, 30));
textfield2.string_("");
textfield2.font_(Font("Helvetica-Bold", 14));
scramblebutton = SCButton(win, Rect(10,10, 200, 30))
.states_([
["change friends color",Color.black,Color.clear]]
)
.action_({
friends.do({arg friend; friend.changeColor(Color.rand)});
});
friends = List.new;
}
speak_{arg string;
textfield.string_(string);
}
speak2_{arg string;
textfield2.string_(string);
}
updateGUI {
win.refresh;
}
addFriend {arg friend;
friends.add(friend);
}
getName {
^name; // note the return symbol
}
setName_ {arg newname; // note the underscore used when you are setting
name = newname;
}
removeFriend {arg friend;
var friendindex;
friendindex = friends.indexOfEqual(friend);
friends.remove(friendindex);
}
showFriends {
var namesOfFriends;
namesOfFriends = List.new;
friends.do({arg friend; namesOfFriends.add(friend.getName)});
textfield2.string_(namesOfFriends.asString);
}
getFriends {
^friends
}
getFriendNames {
var namesOfFriends;
namesOfFriends = List.new;
friends.do({arg friend; namesOfFriends.add(friend.getName)});
^namesOfFriends;
}
changeColor {arg color;
win.view.background_(color);
win.update;
}
}
{language= JavaScript, line-numbers=off}
And here is some code to try the class
a = SonicArtsClass("john", Rect(50, 800, 300, 200), Color.red)
a.speak_("Hi! I'm John")
a.profession = "singer"
a.speak2_("I am a" + a.profession)
b = SonicArtsClass("george", Rect(350, 800, 300, 200), Color.blue)
b.speak_("Hi! I'm george")
b.profession = "bass player"
b.speak2_("I am a" + b.profession)
c = SonicArtsClass("paul", Rect(650, 800, 300, 200), Color.green)
c.speak_("Hi! I'm paul")
c.profession = "guitarist"
c.speak2_("I am a" + c.profession)
// let's fix the roles
b.profession = "guitarist"
b.speak2_("I am a" + b.profession)
c.profession = "bass player"
c.speak2_("I am a" + c.profession)
a.addFriend(b)
a.addFriend(c)
a.showFriends
b.showFriends
c.showFriends
b.addFriend(a)
b.addFriend(c)
b.showFriends // check his friends
// what if john wants to change his name?
a.setName_("ringo");
a.speak_("Hi! I'm"+a.getName)
// we can get the name like this
a.getName
// but not like this:
a.name
// however, we can get the profession like this
a.profession
// WHY?
// the reason is the < (get) and > (set) properties of the profession variable