Hi everyone, this video shows some advanced points on classes.
It has three main parts.
Firstly, how to share a state between instances of a class and its subclasses.
Secondly, how to enable classes to have their own variables.
Thirdly, how to initialize classes.
First up, how to share a state between instances.
Here we have a class with several instances.
To share a state, these instances require a common object.
In Java, we use an "instance" variable that takes the keyword "static."
The value of a static variable
is the same for all instances.
In Pharo, we use "class variables."
This should be a familiar expression that allows us to create classes.
Here we create the Color class, which is a subclass of Object.
First, we list its instance variables, then take a look at the line below.
It lists the class variables of the Color class.
Color class has at least two variables, ColorRegistry and ComponentMask.
A class variable's values
are shared between all instances of the class and subclasses.
These variables are accessible from both instance and class methods.
They start with a capital letter.
Let's go back to our example.
Here we have our Color class,
which is an instance of the metaclass Color class.
The Color class defines two standard variables, rgb and alpha.
These instance variables are private,
meaning that they're only accessible using Color class methods.
It also defines ColorRegistry, which is underlined and uses capitals.
This indicates that this variable is shared.
It's a class variable that is accessible
by the methods of the Color class and those of the class Color class.
Here are some examples of uses of this variable.
In one instance method, that of privateBlue in the Color class,
we access the class variable using its name.
Likewise, if we want to give this variable a value,
we use a colon-equals sign, as with any variable.
We can do this on the instance or class side.
Very often,
the values of these class variables can be read
within instance methods,
and are written within class methods.
This is what occurs most frequently, it's not obligatory.
Now let's look at instance variables of classes.
It's not the same thing.
A class is an object, like everything else.
Like all objects, a class can have instances.
The metaclass describes the class and lists the instance variables
that can only be accessed by its sole instance, its class.
To define an instance variable on the class side,
we click on the "class" button in the code browser.
That gives us the expression here.
So, now I'm browsing the RPackageSet class.
The metaclass of this class defines a variable named cachePackages.
These variables can only be accessed from class methods
and they always start with a lowercase letter.
Here's an example.
The variable cachePackages is defined in the metaclass RPackageSet class.
This means that
the RPackageSet class, which is an instance of the metaclass,
has a value associated with this variable.
Similarly,
all instances of the subclasses of the RPackageSet class
have a specific value for that variable, which is a different value.
There's no sharing here.
Each instance of the RPackageSet class or its subclasses
has its own value for this variable.
Class variables and instance variables on the class side are different things.
We'll explain this using the Singleton Design Pattern,
whose purpose is to ensure that a class has only one instance.
One solution for executing this Pattern is to store, within a variable,
the instance that is freely accessible and disable creation of a new instance.
That's what we'll do here with WebServer.
Here we use an instance variable on the class side.
Its name takes lower case.
This variable is defined in the definition of the metaclass.
Now we disable execution of the "new" method.
No messages can be sent to WebServer class to create new instances.
It's imperative to go via uniqueInstance,
which either returns the variable's value if it's not nil and has content,
or else it adds something to the variable using "super new."
It creates a new instance of WebServer class,
and stores it in the uniqueInstance variable.
The fact that we use class instance variables
leads to the following result.
Each subclass of the WebServer class
will have its own value
for uniqueInstance.
So, if WebServer has three subclasses,
the uniqueInstance variable will have a total of four values.
One for WebServer and for each subclass.
If we now use a class variable,
we edit the class on the instance side
and we add UniqueInstance, using a capital U.
Again we disallow sending "new" messages and implement UniqueInstance.
The only difference is that we use a capital U.
As a result of choosing a class variable,
the class hierarchy has only one singleton.
If WebServer has three subclasses,
these three subclasses and WebServer share the same singleton.
Thus we have one instance shared by all.
Now for class initialization.
We can have variables shared between a class and its instances,
but at some point we must give them a value.
We do this when we initialize the class.
Objects are initialized when the initialize message is sent.
Likewise, we can send the initialize message to a class.
We decide how to initialize variables using the initialize method code.
If we want to initialize the Color class,
we send the initialize message to it.
Typically, when a class is loaded in the system
by the version control tool,
the initialize message is sent to all classes.
This is automatic, there's no need to send the message
manually to all classes loaded.
But if we implement a class,
if we create a new class using the initialize method
we must send the message manually.
Here's an example in the Color class.
We can see we're on the metaclass side.
Here we initialize several class variables,
or instance variables on the class side.
There are both types of variable here.
In the initialize methods,
we never use super initialize on the class side.
When we add an initialize method on the instance side,
we systematically call super initialize
to initialize all instance variables of a newly created object.
But for classes,
since classes and superclasses exist when the initialize message is sent,
we don't call super initialize in the initialize method
on the class side to avoid re-initializing classes.
Here are the takeaways.
We use class variables to share a state.
Classes are objects that can have their own variables,
so we use instance variables on the class side.
To initialize a class, we send it an initialize message
and apply the initialize method on the class side.