Hi everyone, in this video we'll talk about the Anti-IF Campaign.
In particular, we'll explain the reasons why
the Return Nil and Null Check functions are not great.
Here's an example of a code, which is not an object.
Its method parameter is an animal.
Its actions vary according to the animal.
For example, we can ask a dog to wag its tail or a duck to quack.
For a cat, we request other actions.
So, why is it problematic to use If statements?
Particularly when they're used to check the type of receiver.
For example, if we want to add a new animal here,
we would need to check the entire project code
to search for If statements that may apply.
We would have to modify numerous codes throughout the project.
Additionally, adding cases to methods makes them cumbersome,
and they become lost in too many details.
Adding animals makes this method very long,
and each animal type can have many details.
Even for a simple feature, such as not all dogs having tails,
we would have to create separate cases for each option.
The code would become complicated and harder to understand.
So, to replace these actions, we send messages.
This is a point we keep reiterating.
The key point to remember is the sending of messages.
We can replace the previous code with this one.
It applies the showHappiness method in each relevant class.
Each class will decide how each animal will show its "happiness."
For each animal, all we need to do is send the message
showHappiness to the animal,
and one of its methods will be executed.
We see here that Pharo is pursuing the If function.
Pharo decides which method to apply to a particular type of animal.
This is executed automatically.
There's no need for us to specify Ifs for object types.
It only makes codes less coherent and dynamic.
Now we'll discuss the specific case of Nil.
If a method returns nil,
you'll oblige your clients to use If statements.
Whereas, using If is rarely recommended.
Here we use an example of a code
based on a parameter and an inferencer.
The type of code is not important.
Here we see that in some cases, nil is returned.
This means that when we use this code,
we need to test the message rulesForFact.
Did rulesForFact return nil?
We act differently depending on the reply.
We see that in this case,
since we're using a plural term,
the method will probably return a collection.
An effective solution for avoiding nil
in this situation is to return an empty collection.
This works in many cases.
Returning an empty collection instead of nil simplifies the code.
Because clients can simply iterate the collection,
and if it's empty, nothing will happen.
For exceptional cases,
such as when you have a file stream
that has not been opened for writing and shows an error,
instead of returning nil, we inform the system by raising an exception.
In Pharo, we call this filing an exception.
We create an instance of the Exception class or subclass
and send the message, or signal.
This avoids obliging the client of the method nextPutAll
to test if it is nil, when a problem has likely occurred.
Either the client handles the exception
or it's handled by the client of the client, and so on.
We can focus on one specific level to capture the exception.
It avoids overuse of Ifs.
Another case where we find checks for the nil value
is in instance variables that are not initialized.
If a code says that if the variable is still nil, it must react a certain way,
it's better to initialize the variable straightaway,
with a value that works for all cases.
So, here,
for "members," which contains a collection,
we initialize an empty collection instead of using nil.
Once again, this often works well.
If you want to give a value to a variable,
and if it's costly to calculate its value,
you can wait until the last moment to calculate it.
It may never be calculated, so it saves execution time.
In such cases, we use lazy initialization.
This is used when a value is required.
If the value is still nil, we assign it a value.
If it's no longer nil, we return its value immediately.
Here we have an If associated with nil, but we have only one.
All other users of the variable utilize the descent method,
and have not tested if it is nil.
Sometimes we come across cases
in which it's necessary to check whether or not we need to respond.
As we see in this example.
Here we have a ToolPalette.
If a tool is selected, we can respond,
but if none is selected, we prefer not to act.
Look at the selectedTool function.
If it returns nil, no tools are selected, so no action is required.
If selectedTool returns something,
we will ask it to perform an action.
A good way of replacing this
is to use the NullObject pattern.
Instead of having two cases, one with tools and one without,
we have one case in which one of our tools does nothing.
This tool will be selected by default.
We create a tool that does nothing when asked to perform actions.
Instead of not selecting anything, we enable a tool that does nothing.
To find out more about NullObject, see these references.
In conclusion,
messages are more effective than Ifs.
You will utilize Ifs in certain cases,
but you can often avoid using them and send messages instead.
Avoid returning nil because it obliges you to insert If checks
to find out whether or not the value is nil.
Initialize variables either on creation or using lazy initialization.
Create objects representing default behavior or an absence of behavior.
This applies not only to Pharo but to all object languages.
It's important to remember these points whichever language you use.