Hello.
Today we are going to talk about messages.
You'll see why having lots of messages and small methods is good.
Contrary to what many developers believe.
This slide illustrates a design sequence.
This is valid not only for Pharo,
but for design in any object-oriented language.
As you know, message sends are hooks,
places where we can define behavior.
Developers often say that they like big methods because
they're easy to understand.
You read the lines and you understand the code.
But in this session I will show you
that this is not such a good thing.
In general, big methods mean bad design.
A hierarchy of classes
and a same method implemented in several classes,
is one means of defining choices.
If I take a fat class with lots of operations,
and I have to choose an operation according to status,
my code will say: "If I'm in this state, I do this operation.
"If I'm in that state, I do that operation."
You'll have big codes
with IFs all over the place.
This means that when you want to add new cases,
you have to modify methods everywhere.
In the version on the right-hand side,
the operation for each case is divided into a specific class.
All you have to do is send an operation message
to an object to create an IF.
The IF does not need to be written by the programmer.
It's done automatically via the polymorphism principle.
In the following slides, I'll show you how to improve one example
by changing small bits at a time.
Here is a big method that is not easy to understand
and which does many things.
Here's what we want to do.
In a subclass,
we want to give this variable here a different value.
As it's set up,
the only way to do that is to create the subclass,
then duplicate all of the code,
adding the small modification you want to make.
In languages like Java
that use private keywords,
if the attributes used by the method are private,
what I just did is impossible.
If the method uses instance variables that are private,
subclasses cannot duplicate code.
But in any case, duplicating is not good practice,
because duplication copies bugs too.
If I have a bug in my original version, I'll have a bug in my copies.
And if I modify one copy, I have to modify every duplication.
It's extra work for the developer
and you must remember that there are various copies.
The real solution is sending messages.
In a method, when you send a message rather than writing
the content of the corresponding method directly inside,
subclasses can override behavior.
If we look at the bar method,
it sends foo to self.
In A, foo returns 10,
but the subclasses can override this value
and replace it with 42, for example.
So how can we improve the code you just saw
and benefit from the inheritance mechanism
without using duplication?
I'm going to extract this part here and send a message instead.
It's done by a refactoring function called extract method.
You have tools to transform this code into this code here.
The code I selected in the previous slide
was transferred into a new method called ratio.
And here where the code was written, we now have a message send.
This means that in the subclasses,
I can change this behavior.
Either change it completely
or recall the behavior of the superclass and make a modification.
That's what I'm doing here.
I send a ratio message to super
to execute the code as it is in the superclass,
and I add 10, which is what my goal was.
Another possible method is to extract this part here,
so that the subclasses can change this behavior.
I extract this piece of code
into a specific method.
And in the primary method, I send a message.
In this case,
the class we want an instance variable for is in hardcode.
This means that if the subclasses want to change this class,
to potentially get a UINode subclass,
it will have to duplicate the entire method.
Here we can use the same process
and extract the class into a method,
so that the subclasses can change the instance variable.
That's what I'm doing here.
I extract the part that interests me into a method,
and I send a message.
Sending a message enables the subclasses to change behavior.
Like I said earlier, certain programmers
do not agree with this approach.
They find it difficult to read lots of small, spread out methods.
They'd rather read a big method line by line.
This isn't good practice, because reading code line by line
won't work if the application is very big.
You can't read line by line and understand what's going on.
This is where abstractions are useful and extracting bits of behavior
or expressions from methods makes sense.
You can read a method globally without understanding every detail.
So little methods are good, you should use them everywhere.
Let's carry on.
Here we see that the value 55 appears in hardcode
in the method code.
This means the subclasses cannot change to 100, for example.
So we will extract this value like we did previously,
and put it in a separate method so that the subclasses can change the value.
Another advantage is that previously
the value 55 was written here.
Now it's called self averageRatio.
This means we replaced a numerical value by a name,
and I now know what the value 55 corresponds to.
I know it means averageRatio and when I read the code, I understand.
So having many small methods helps to read the code.
Creating an averageRatio method to return 55
enables all subclasses to change values.
But how can we let class users change their value too?
What we can do,
is use an instance variable.
The averageRatio method used in the previous slide
will return the value of the instance variable,
if it contains a value. If not, it returns the default value.
The default value was 55.
And the users of a node object
can program whatever value they like inside.
With this design,
subclasses can override the defaultAverageRatio
and change the value.
And class users can set a value by executing the program.
This is called gruyere-oriented programming.
An object-oriented program, a WIN method,
contains holes called hooks,
which can be filled with subclasses.
As a conclusion,
code can be reused and refined in subclasses.
Each time we send a message,
subclasses can change the behavior of the superclass:
Refine it or completely change it.
Small methods are great because they give names to bits of expressions,
and because they give subclasses the freedom to change behavior.