-In this lecture, I would like to show
that object-oriented design models the world in a different fashion.
First, what you should know
is that there is not one single model of the world.
It depends on your focus of attention.
For example, in a bank, they model the risk in a different way
than in an insurance company, because in an insurance company,
it is the key business aspect, whereas in a bank, it is a side effect.
So, on the one hand, you do not have a unique model of the world,
but in addition, you have different means and ways
to model the world.
In the past, we could use data-driven models,
that are more used in database systems,
object-oriented design, procedural modeling and so on.
In this lecture, we will compare procedural modeling
and object-oriented programming.
What is object orientation?
In fact, object orientation is a paradigm in that it is not a technology.
Object-oriented design is a way to represent the world
by simulating the world
as a set of collaborating and decentralized entities.
The idea is to try to minimize the complexity
or to handle the complexity of what you are modeling,
and also increase the reusability and lower the maintenance cost.
Let us compare procedural design and object-oriented design.
In procedural design,
the system will focus on structures and procedures.
Data is shared between procedures
and is accessible from all procedures, even client procedures,
and procedures know about the structure of data.
There is not this notion of late binding.
This means that even if we can simulate it,
in fact, procedures have different name
that are into a kind of single namespace like in C.
And there is no decoupling between messages and methods.
They are just procedures accessing data.
Let us take an example to compare procedural modeling
and object-oriented modeling.
Imagine that we have a picture which is composed of shapes,
like squares, rectangles and circles,
and we want to compute the area of the picture.
In the procedural way,
and I wrote it in Java because you can write,
in an object-oriented language, in a procedural way,
what we will do is simple.
We will iterate over the list of shapes, and for each shape,
we will check: "If there is a square, then I know how to compute it.
It is width multiplied by width.
If there is a rectangle,
I have to multiply the length by the height.
And if there is a circle, I multiply pi by radius squared
and I am done."
We can also write that in Pharo.
I can iterate over the shape and I do exactly the same.
And I check: "If this class is from that kind, then I do that."
If this class is from that kind, then I do that."
Now, what are the drawbacks? There are several.
First, all the logic is defined in one place,
which is not bad per say.
But the implications are bad.
I cannot reuse the main functionality.
What happens if I want to add a new shape?
If I want to add a new shape, I have to modify this method
or this procedure, and I have to recompile it.
And I cannot even reuse the main procedure.
Now, let us look at how this is done in the OOP way.
In the OOP way, I will define the area in the class "picture"
as a method, and I will iterate over the shapes.
And I will ask each shape to compute its own area.
And then, on square, rectangle and circle,
I will define a method "area" that computes its area.
What are the advantages? Adding a new shape is really easy.
I just add a class with the message "area" and I am done.
I can create objects and add them to the diagram and I am done.
I can reuse the "Picture >> area" method.
There is no problem, because it is independent of the shapes.
And then, we can see that there is a decentralized view of computation.
Each shape represents its own data/logic internally
and exposes just the area to the rest.
But there is a catch.
Imagine, with object-oriented programming,
if we had not given the same name to the method "area",
then, we could not reuse the "Picture >> area" functionality
because then, the client would have to check.
So, all the shapes that offer the same service
agree to expose the same API, the same interface to the client.
This is what is called "polymorphism".
This means that different objects can answer the same message
with different executions.
This is really important, because it is the way
to build good object-oriented design by creating substitutable objects.
At the end, what is an application in an object-oriented system?
An application is a collection of interacting entities, objects.
Objects are characterized by behavior and by state.
The state is private and only methods can access the state.
Objects communicate by messages that are resolved dynamically.
There is a distinction between the messages
and the methods that are executed.
And message passing is late bound,
so it depends on the receiver of the object
and the computation that is attached to this object via its class.
What we saw is that objects are unique entities.
They have a private state, they react to messages,
and their methods perform computation.
The key point is that objects should offer polymorphic interfaces
so that they can be mixed with other objects
and that we can reuse the code that used them.
Now, I would like to go over three cornerstones
of object-oriented programming and show the interplay between them.
The first two are encapsulation.
Encapsulation is the ability to hide the way computation is done
and data is stored.
So, when I send a message, as a client,
I do not care about the way my object manages data
and how computation is performed.
So, from that perspective,
if an object is composed and delegates to other objects,
I am not impacted, as a client.
This means that the object is free to decompose
its own computation into a subtask, in a decentralized way.
This interplay with encapsulation and composition is really important.
Then, there is another cornerstone, the distribution of responsibility.
This means that when I compute a problem, I decompose the problem into subtasks.
And this decomposition is emphasized by the use of late binding,
where the idea is that the receiver will decide what to do.
And these two things are also further emphasized
by the idea of polymorphism.
Because I have objects that exhibit the same interface,
then I can mix them together.
Because I have late binding, then I can have polymorphism,
and then I can reuse objects and build more modular applications.
The third cornerstone that I have not presented yet
is an important one.
It is the idea that I can build, in object-oriented programming,
abstraction by extending other abstractions.
So, the point is that I can define a class as a delta to another one.
For example, if I have a "collection" class
that manages collections of objects,
then I can refine this collection of objects
as "OrderedCollection", because there is an order.
An "Array", for example, can be a kind of collection.
And this mechanism to define an abstraction
by specifying the delta from one abstraction to another one
is called "inheritance".
Inheritance is super powerful to reuse codes from superclasses.
Here, for example, the code of collection will be reused
in "OrderedCollection" and "Array".
But I did not cover it in this lecture because I will in another one.
So, what have we shown?
That with object-oriented programming,
programs are collaborating entities that exchange messages,
that objects encapsulate their private data,
that they can and should expose polymorphic APIs or interfaces
so that they can be mixed with other objects
and the client code can be reused.
And we saw that late binding is a key aspect
that enables all this dynamic.
And at the end, we get more modular applications.