 |
 |
1) Introduction sur les « objets » |
|
 |
|
Texte original |
 |
Traducteur : Jérome QUELIN |
|
 |
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
Java uses the second approach,
exclusively[7]. Every
time you want to create an object, you use the new keyword to build a
dynamic instance of that object.
|
 |
Java utilise exclusivement la deuxième approche name="fnB7">[7]. Le mot clef new est utilisé pour créer une
instance dynamique d'un objet à chaque fois qu'on en a besoin.
|
 |
 |
 |
There's another issue, however, and
that's the lifetime of an object. With languages that allow objects to be
created on the stack, the compiler determines how long the object lasts and can
automatically destroy it. However, if you create it on the heap the compiler has
no knowledge of its lifetime. In a language like C++, you must determine
programmatically when to destroy the object, which can lead to memory leaks if
you don’t do it correctly (and this is a common problem in C++ programs).
Java provides a feature called a garbage collector that automatically discovers
when an object is no longer in use and destroys it. A garbage collector is much
more convenient because it reduces the number of issues that you must track and
the code you must write. More important, the garbage collector provides a much
higher level of insurance against the insidious problem of memory leaks (which
has brought many a C++ project to its knees).
|
 |
Une autre particularité importante est la durée de vie d'un
objet. Avec les langages qui autorisent la création d'objets dans la pile, le compilateur
détermine combien de temps l'objet est amené à vivre et peut le
détruire automatiquement. Mais si l'objet est créé dans le segment, le
compilateur n'a aucune idée de sa durée de vie. Dans un langage comme le C++, il faut
déterminer dans le programme quand détruire l'objet, ce qui peut mener à des
fuites de mémoire si cela n'est pas fait correctement (et c'est un problème courant
en C++). Java propose une fonctionnalité appelée ramasse-miettes (garbage collector)
qui découvre automatiquement quand un objet n'est plus utilisé et le détruit.
Java propose donc un niveau plus élevé d'assurance contre les fuites de
mémoire. Disposer d'un ramasse-miettes est pratique car cela réduit le code à
écrire et, plus important, le nombre de problèmes liés à la gestion de
la mémoire (qui ont mené à l'abandon de plus d'un projet C++).
|
 |
 |
 |
The rest of this section looks at
additional factors concerning object lifetimes and
landscapes.
|
 |
Le reste de cette section s'attarde sur des facteurs additionnels
concernant l'environnement et la durée de vie des objets.
|
 |
 |
 |
Collections and iterators
|
 |
Collections et itérateurs
|
 |
 |
 |
If you don’t know how many objects
you’re going to need to solve a particular problem, or how long they will
last, you also don’t know how to store those objects. How can you know how
much space to create for those objects? You can’t, since that information
isn’t known until run-time.
|
 |
Si le nombre d'objets nécessaires à la résolution d'un
problème est inconnu, ou combien de temps on va en avoir besoin, on ne peut pas non plus
savoir comment les stocker. Comment déterminer l'espace nécessaire pour créer
ces objets ? C'est impossible car cette information n'est connue que lors de
l'exécution.
|
 |
 |
 |
The solution to most problems in
object-oriented design seems flippant: you create another type of object. The
new type of object that solves this particular problem holds references to other
objects. Of course, you can do the same thing with an array, which is available
in most languages. But there’s more. This new object, generally called a
container (also called a collection, but the Java library uses
that term in a different sense so this book will use “container”),
will expand itself whenever necessary to accommodate everything you place inside
it. So you don’t need to know how many objects you’re going to hold
in a container. Just create a container object and let it take care of the
details.
|
 |
La solution à la plupart des problèmes en conception
orientée objet est simple : il suffit de créer un nouveau type d'objet. Le
nouveau type d'objets qui résout ce problème particulier contient des
références aux autres objets. Bien sûr, un tableau ferait aussi bien l'affaire.
Mais il y a plus. Ce nouvel objet, appelé conteneur (ou collection, mais
la bibliothèque Java utilise ce terme dans un autre sens ; nous utiliserons donc le
terme « conteneur » dans la suite de ce livre), grandira automatiquement pour
accepter tout ce qu'on place dedans. Connaître le nombre d'objets qu'on désire stocker
dans un conteneur n'est donc plus nécessaire. Il suffit de créer un objet conteneur
et le laisser s'occuper des détails.
|
 |
 |
 |
Fortunately, a good OOP language comes
with a set of containers as part of the package. In C++, it’s part of the
Standard C++ Library and is sometimes called the Standard Template Library
(STL). Object Pascal has containers in its Visual Component Library (VCL).
Smalltalk has a very complete set of containers. Java also has containers in its
standard library. In some libraries, a generic container is considered good
enough for all needs, and in others (Java, for example) the library has
different types of containers for different needs: a vector (called an
ArrayList in Java) for consistent access to all elements, and a linked
list for consistent insertion at all elements, for example, so you can choose
the particular type that fits your needs. Container libraries may also include
sets, queues, hash tables, trees, stacks, etc.
|
 |
Heureusement, les langages orientés objet décents fournissent
ces conteneurs. En C++, ils font partie de la bibliothèque standard (STL, Standard Template
Library). Le Pascal Objet dispose des conteneurs dans sa Bibliothèque de Composants Visuels
(VCL, Visual Component Library). Smalltalk propose un ensemble vraiment complet de conteneurs. Java
aussi propose des conteneurs dans sa bibliothèque standard. Dans certaines
bibliothèques, un conteneur générique est jugé suffisant pour tous les
besoins, et dans d'autres (Java par exemple), la bibliothèque dispose de différents
types de conteneurs suivant les besoins : des vecteurs (appelé
ArrayList en Java) pour un accès pratique à tous les
éléments, des listes chaînées pour faciliter l'insertion, par exemple,
on peut donc choisir le type particulier qui convient le mieux. Les bibliothèques de
conteneurs peuvent aussi inclure les ensembles, les files, les dictionnaires, les arbres, les
piles, etc...
|
 |
 |
 |
All containers have some way to put
things in and get things out; there are usually functions to add elements to a
container, and others to fetch those elements back out. But fetching elements
can be more problematic, because a single-selection function is restrictive.
What if you want to manipulate or compare a set of elements in the container
instead of just one?
|
 |
Tous les conteneurs disposent de moyens pour y stocker des choses et les
récupérer ; ce sont habituellement des fonctions pour ajouter des
éléments dans un conteneur et d'autres pour les y retrouver. Mais retrouver des
éléments peut être problématique, car une fonction de sélection
unique peut se révéler trop restrictive. Comment manipuler ou comparer un ensemble
d'éléments dans le conteneur ?
|
 |
 |
 |
The solution is an iterator, which is an
object whose job is to select the elements within a container and present them
to the user of the iterator. As a class, it also provides a level of
abstraction. This abstraction can be used to separate the details of the
container from the code that’s accessing that container. The container,
via the iterator, is abstracted to be simply a sequence. The iterator allows you
to traverse that sequence without worrying about the underlying
structure—that is, whether it’s an ArrayList, a
LinkedList, a Stack, or something else. This gives you the
flexibility to easily change the underlying data structure without disturbing
the code in your program. Java began (in version 1.0 and 1.1) with a standard
iterator, called Enumeration, for all of its container classes. Java 2
has added a much more complete container library that contains an iterator
called Iterator that does more than the older
Enumeration.
|
 |
La réponse à cette question prend la forme d'un
itérateur, qui est un objet dont le travail est de choisir les éléments d'un
conteneur et de les présenter à l'utilisateur de l'itérateur. En tant que
classe, il fournit de plus un niveau d'abstraction supplémentaire. Cette abstraction peut
être utilisée pour séparer les détails du conteneur du code qui utilise
ce conteneur. Le conteneur, via l'itérateur, est perçu comme une séquence.
L'itérateur permet de parcourir cette séquence sans se préoccuper de sa
structure sous-jacente - qu'il s'agisse d'une ArrayList (vecteur), une
LinkedList (liste chaînée), une Stack (pile) ou
autre. Cela permet de changer facilement la structure de données sous-jacente sans perturber
le code du programme. Java commença (dans les versions 1.0 et 1.1) avec un itérateur
standard, appelé Enumeration, pour toutes ses classes conteneurs. Java 2
est accompagné d'une bibliothèque de conteneurs beaucoup plus complète qui
contient entre autres un itérateur appelé Iterator bien plus
puissant que l'ancienne Enumeration.
|
 |
 |
 |
From a design standpoint, all you really
want is a sequence that can be manipulated to solve your problem. If a single
type of sequence satisfied all of your needs, there’d be no reason to have
different kinds. There are two reasons that you need a choice of containers.
First, containers provide different types of interfaces and external behavior. A
stack has a different interface and behavior than that of a queue, which is
different from that of a set or a list. One of these might provide a more
flexible solution to your problem than the other. Second, different containers
have different efficiencies for certain operations. The best example is an
ArrayList and a LinkedList. Both are simple sequences that can
have identical interfaces and external behaviors. But certain operations can
have radically different costs. Randomly accessing elements in an
ArrayList is a constant-time operation; it takes the same amount of time
regardless of the element you select. However, in a LinkedList it is
expensive to move through the list to randomly select an element, and it takes
longer to find an element that is further down the list. On the other hand, if
you want to insert an element in the middle of a sequence, it’s much
cheaper in a LinkedList than in an ArrayList. These and other
operations have different efficiencies depending on the underlying structure of
the sequence. In the design phase, you might start with a LinkedList and,
when tuning for performance, change to an ArrayList. Because of the
abstraction via iterators, you can change from one to the other with minimal
impact on your code.
|
 |
Du point de vue du design, tout ce dont on a besoin est une séquence
qui peut être manipulée pour résoudre le problème. Si un seul type de
séquence satisfaisait tous les besoins, il n'y aurait pas de raison d'en avoir de types
différents. Il y a deux raisons qui font qu'on a besoin d'un choix de conteneurs. Tout
d'abord, les conteneurs fournissent différents types d'interfaces et de comportements. Une
pile a une interface et un comportement différents de ceux d'une file, qui sont
différents de ceux fournis par un ensemble ou une liste. L'un de ces conteneurs peut se
révéler plus flexible qu'un autre pour la résolution du problème
considéré. Deuxièmement, les conteneurs ne sont pas d'une même
efficacité pour les mêmes opérations. Prenons le cas d'une
ArrayList et d'une LinkedList. Les deux sont de simples
séquences qui peuvent avoir la même interface et comportement. Mais certaines
opérations ont des coûts radicalement différents. Accéder à des
éléments au hasard dans une ArrayList est une opération qui
demande toujours le même temps, quel que soit l'élément auquel on souhaite
accéder. Mais dans une LinkedList, il est coûteux de se
déplacer dans la liste pour rechercher un élément, et cela prend plus de temps
pour trouver un élément qui se situe plus loin dans la liste. Par contre, si on
souhaite insérer un élément au milieu d'une séquence, c'est bien plus
efficace dans une LinkedList que dans une ArrayList. Ces
opérations et d'autres ont des efficacités différentes suivant la structure
sous-jacente de la séquence. Dans la phase de conception, on peut débuter avec une
LinkedList et lorsqu'on se penche sur l'optimisation, changer pour une
ArrayList. Grâce à l'abstraction fournie par les itérateurs,
on peut passer de l'une à l'autre avec un impact minime sur le code.
|
 |
 |
 |
In the end, remember that a container is
only a storage cabinet to put objects in. If that cabinet solves all of your
needs, it doesn’t really matter how it is implemented (a basic concept
with most types of objects). If you’re working in a programming
environment that has built-in overhead due to other factors, then the cost
difference between an ArrayList and a LinkedList might not matter.
You might need only one type of sequence. You can even imagine the
“perfect” container abstraction, which can automatically change its
underlying implementation according to the way it is
used.
|
 |
En définitive, un conteneur n'est qu'un espace de stockage où
placer des objets. Si ce conteneur couvre tous nos besoins, son implémentation réelle
n'a pas grande importance (un concept de base pour la plupart des objets). Mais il arrive que la
différence de coûts entre une ArrayList et une
LinkedList ne soit pas à négliger, suivant l'environnement du
problème et d'autres facteurs. On peut n'avoir besoin que d'un seul type de séquence.
On peut même imaginer le conteneur « parfait », qui changerait
automatiquement son implémentation selon la manière dont on l'utilise.
|
 |
 |
 |
The singly rooted hierarchy
|
 |
La hiérarchie de classes unique
|
 |
 |
 |
One of the issues in OOP that has become
especially prominent since the introduction of C++ is whether all classes should
ultimately be inherited from a single base class. In Java (as with virtually all
other OOP languages) the answer is “yes” and the name of this
ultimate base class is simply Object. It turns out that the benefits of
the singly rooted hierarchy are many.
|
 |
L'une des controverses en POO devenue proéminente depuis le C++
demande si toutes les classes doivent être finalement dérivées d'une classe de
base unique. En Java (et comme dans pratiquement tous les autres langages OO) la réponse est
« oui » et le nom de cette classe de base ultime est tout simplement
Object. Les bénéfices d'une hiérarchie de classes unique sont
multiples.
|
 |
 |
 |
All objects in a singly rooted hierarchy
have an interface in common, so they are all ultimately the same type. The
alternative (provided by C++) is that you don’t know that everything is
the same fundamental type. From a backward-compatibility standpoint this fits
the model of C better and can be thought of as less restrictive, but when you
want to do full-on object-oriented programming you must then build your own
hierarchy to provide the same convenience that’s built into other OOP
languages. And in any new class library you acquire, some other incompatible
interface will be used. It requires effort (and possibly multiple inheritance)
to work the new interface into your design. Is the extra
“flexibility” of C++ worth it? If you need it—if you have a
large investment in C—it’s quite valuable. If you’re starting
from scratch, other alternatives such as Java can often be more
productive.
|
 |
Tous les objets dans une hiérarchie unique ont une interface
commune, ils sont donc tous du même type fondamental. L'alternative (proposée par le
C++) est qu'on ne sait pas que tout est du même type fondamental. Du point de vue de la
compatibilité ascendante, cela épouse plus le modèle du C et peut se
révéler moins restrictif, mais lorsqu'on veut programmer en tout objet il faut
reconstruire sa propre hiérarchie de classes pour bénéficier des mêmes
avantages fournis par défaut par les autres langages OO. Et dans chaque nouvelle
bibliothèque de classes qu'on récupère, une interface différente et
incompatible sera utilisée. Cela demande des efforts (et éventuellement l'utilisation
de l'héritage multiple) pour intégrer la nouvelle interface dans la conception.
Est-ce que la « flexibilité » que le C++ fournit en vaut
réellement le coup ? Si on en a besoin - par exemple si on dispose d'un gros
investissement en C - alors oui. Mais si on démarre de zéro, d'autres alternatives
telles que Java se révèlent beaucoup plus productives.
|
 |
 |
 |
All objects in a singly rooted hierarchy
(such as Java provides) can be guaranteed to have certain functionality. You
know you can perform certain basic operations on every object in your system. A
singly rooted hierarchy, along with creating all objects on the heap, greatly
simplifies argument passing (one of the more complex topics in
C++).
|
 |
Tous les objets dans une hiérarchie de classes unique (comme celle
que propose Java) sont garantis d'avoir certaines fonctionnalités. Un certain nombre
d'opérations élémentaires peuvent être effectuées sur tous les
objets du système. Une hiérarchie de classes unique, accompagnée de la
création des objets dans le segment, simplifie considérablement le passage
d'arguments (l'un des sujets les plus complexes en C++).
|
 |
 |
 |
A singly rooted hierarchy makes it much
easier to implement a garbage collector (which is conveniently built into Java).
The necessary support can be installed in the base class, and the garbage
collector can thus send the appropriate messages to every object in the system.
Without a singly rooted hierarchy and a system to manipulate an object via a
reference, it is difficult to implement a garbage collector.
|
 |
Une hiérarchie de classes unique facilite aussi
l'implémentation d'un ramasse-miettes (qui est fourni en standard en Java). Le support
nécessaire est implanté dans la classe de base, et le ramasse-miettes peut donc
envoyer le message idoine à tout objet du système. Sans une hiérarchie de
classe unique et un système permettant de manipuler un objet via une
référence, il est difficile d'implémenter un ramasse-miettes.
|
 |
 |
 |
Since run-time type information is
guaranteed to be in all objects, you’ll never end up with an object whose
type you cannot determine. This is especially important with system level
operations, such as exception handling, and to allow greater flexibility in
programming.
|
 |
Comme tout objet dispose en lui d'informations dynamiques, on ne peut se
retrouver avec un objet dont on ne peut déterminer le type. Ceci est particulièrement
important avec les opérations du niveau système, telles que le traitement des
exceptions, et cela permet une plus grande flexibilité dans la programmation.
|
 |
 |
 |
Collection libraries and support for easy collection use
|
 |
Bibliothèques de collections et support pour l'utilisation
aisée des collections
|
 |
 |
 |
Because a container is a tool that
you’ll use frequently, it makes sense to have a library of containers that
are built in a reusable fashion, so you can take one off the shelf and plug it
into your program. Java provides such a library, which should satisfy most
needs.
|
 |
Parce qu'un conteneur est un outil qu'on utilise fréquemment, il est
logique d'avoir une bibliothèque de conteneurs conçus de manière à
être réutilisables, afin de pouvoir en prendre un et l'insérer dans le
programme. Java fournit une telle bibliothèque, qui devrait satisfaire tous les
besoins.
|
 |
 |
 |
Downcasting vs. templates/generics
|
 |
Transtypages descendants vs. patrons génériques
|
 |
 |
 |
To make these containers reusable, they
hold the one universal type in Java that was previously mentioned:
Object. The singly rooted hierarchy means that everything is an
Object, so a container that holds Objects can hold anything. This
makes containers easy to reuse.
|
 |
Pour rendre ces conteneurs réutilisables, ils stockent le type
universel en Java précédemment mentionné : Object. La
hiérarchie de classe unique implique que tout est un Object, un conteneur
stockant des Objects peut donc stocker n'importe quoi. Cela rend les conteneurs
aisément réutilisables.
|
 |
 |
 |
To use such a container, you simply add
object references to it, and later ask for them back. But, since the container
holds only Objects, when you add your object reference into the container
it is upcast to Object, thus losing its identity. When you fetch it back,
you get an Object reference, and not a reference to the type that you put
in. So how do you turn it back into something that has the useful interface of
the object that you put into the container?
|
 |
Pour utiliser ces conteneurs, il suffit d'y ajouter des
références à des objets, et les redemander plus tard. Mais comme le conteneur
ne stocke que des Objects, quand une référence à un objet est
ajoutée dans le conteneur, il subit un transtypage ascendant en Object,
perdant alors son identité. Quand il est recherché par la suite, on
récupère une référence à un Object, et non une
référence au type qu'on a inséré. Comment le récupérer et
retrouver l'interface de l'objet qu'on a stocké dans le conteneur ?
|
 |
 |
 |
Here, the cast is used again, but this
time you’re not casting up the inheritance hierarchy to a more general
type, you cast down the hierarchy to a more specific type. This manner of
casting is called downcasting. With upcasting, you know, for example, that a
Circle is a type of Shape so it’s safe to upcast, but you
don’t know that an Object is necessarily a Circle or a
Shape so it’s hardly safe to downcast unless you know that’s
what you’re dealing with.
|
 |
On assiste ici aussi à un transtypage, mais cette fois-ci il ne
remonte pas dans la hiérarchie de classe à un type plus général, mais
descend dans la hiérarchie jusqu'à un type plus spécifique, c'est un
transtypage descendant ou spécialisation, ou soustypage. Avec la
généralisation, on sait par exemple qu'un Cercle est un type de
Forme, et que le transtypage est donc sans danger ; mais on ne sait pas qu'un
Object est aussi un Cercle ou une Forme, il est
donc rarement sûr d'appliquer une spécialisation à moins de savoir exactement
à quoi on a affaire.
|
 |
 |
 |
It’s not completely dangerous,
however, because if you downcast to the wrong thing you’ll get a run-time
error called an exception, which will be described shortly. When you
fetch object references from a container, though, you must have some way to
remember exactly what they are so you can perform a proper
downcast.
|
 |
Ce n'est pas trop dangereux cependant, car si une spécialisation est
tentée jusqu'à un type incompatible le système d'exécution
générera une erreur appelée exception, qui sera décrite plus
loin. Quand une référence d'objet est rapatriée d'un conteneur, il faut donc
un moyen de se rappeler exactement son type afin de pouvoir le spécialiser
correctement.
|
 |
 |
 |
Downcasting and the run-time checks
require extra time for the running program, and extra effort from the
programmer. Wouldn’t it make sense to somehow create the container so that
it knows the types that it holds, eliminating the need for the downcast and a
possible mistake? The solution is parameterized types, which are classes that
the compiler can automatically customize to work with particular types. For
example, with a parameterized container, the compiler could customize that
container so that it would accept only Shapes and fetch only
Shapes.
|
 |
La spécialisation et les contrôles à l'exécution
génèrent un surcoût de temps pour le programme, et des efforts
supplémentaires de la part du programmeur. Il semblerait plus logique de créer le
conteneur de façon à ce qu'il connaisse le type de l'objet stocké,
éliminant du coup la spécialisation et la possibilité d'erreur. La solution
est fournie par les types paramétrés, qui sont des classes que le compilateur peut
personnaliser pour les faire fonctionner avec des types particuliers. Par exemple, avec un
conteneur paramétré, le compilateur peut personnaliser ce conteneur de façon
à ce qu'il n'accepte que des Formes et ne renvoie que des
Formes.
|
 |
 |
 |
Parameterized types are an important part
of C++, partly because C++ has no singly rooted hierarchy. In C++, the keyword
that implements parameterized types is “template.” Java currently
has no parameterized types since it is possible for it to get by—however
awkwardly—using the singly rooted hierarchy. However, a current proposal
for parameterized types uses a syntax that is strikingly similar to C++
templates.
|
 |
Les types paramétrés sont importants en C++, en particulier
parce que le C++ ne dispose pas d'une hiérarchie de classe unique. En C++, le mot clef qui
implémente les types paramétrés est « template ». Java
ne propose pas actuellement de types paramétrés car c'est possible de les simuler -
bien que difficilement - via la hiérarchie de classes unique. Une solution de types
paramétrés basée sur la syntaxe des templates C++ est actuellement en cours de
proposition.
|
 |
 |
 |
The housekeeping dilemma: who should clean up?
|
 |
Le dilemme du nettoyage : qui en est responsable ?
|
 |
 |
 |
Each object requires resources in order
to exist, most notably memory. When an object is no longer needed it must be
cleaned up so that these resources are released for reuse. In simple programming
situations the question of how an object is cleaned up doesn’t seem too
challenging: you create the object, use it for as long as it’s needed, and
then it should be destroyed. It’s not hard, however, to encounter
situations in which the situation is more complex.
|
 |
Chaque objet requiert des ressources, en particulier de la mémoire.
Quand un objet n'est plus utilisé il doit être nettoyé afin de rendre ces
ressources pour les réutiliser. Dans la programmation de situations simples, la question de
savoir comment un objet est libéré n'est pas trop compliquée : il suffit
de créer l'objet, l'utiliser aussi longtemps que désiré, et ensuite le
détruire. Il n'est pas rare par contre de se trouver dans des situations beaucoup plus
complexes.
|
 |
 |
 |
Suppose, for example, you are designing a
system to manage air traffic for an airport. (The same model might also work for
managing crates in a warehouse, or a video rental system, or a kennel for
boarding pets.) At first it seems simple: make a container to hold airplanes,
then create a new airplane and place it in the container for each airplane that
enters the air-traffic-control zone. For cleanup, simply delete the appropriate
airplane object when a plane leaves the zone.
|
 |
Supposons qu'on veuille concevoir un système pour gérer le
trafic aérien d'un aéroport (ou pour gérer des caisses dans un entrepôt,
ou un système de location de cassettes, ou un chenil pour animaux). Cela semble simple de
prime abord : créer un conteneur pour stocker les avions, puis créer un nouvel
avion et le placer dans le conteneur pour chaque avion qui entre dans la zone de contrôle du
trafic aérien. Pour le nettoyage, il suffit de détruire l'objet avion correspondant
lorsqu'un avion quitte la zone.
|
 |
 |
 |
But perhaps you have some other system to
record data about the planes; perhaps data that doesn’t require such
immediate attention as the main controller function. Maybe it’s a record
of the flight plans of all the small planes that leave the airport. So you have
a second container of small planes, and whenever you create a plane object you
also put it in this second container if it’s a small plane. Then some
background process performs operations on the objects in this container during
idle moments.
|
 |
Mais supposons qu'une autre partie du système s'occupe d'enregistrer
des informations à propos des avions, ces données ne requérant pas autant
d'attention que la fonction principale de contrôle. Il s'agit peut-être d'enregistrer
les plans de vol de tous les petits avions quittant l'aéroport. On dispose donc d'un second
conteneur des petits avions, et quand on crée un objet avion on doit aussi le stocker dans
le deuxième conteneur si c'est un petit avion. Une tâche de fond s'occupe de traiter
les objets de ce conteneur durant les moments d'inactivité du système.
|
 |
 |
 |
Now the problem is more difficult: how
can you possibly know when to destroy the objects? When you’re done with
the object, some other part of the system might not be. This same problem can
arise in a number of other situations, and in programming systems (such as C++)
in which you must explicitly delete an object when you’re done with it
this can become quite complex.
|
 |
Le problème est maintenant plus compliqué : comment savoir
quand détruire les objets ? Quand on en a fini avec un objet, une autre partie du
système peut ne pas en avoir terminé avec. Ce genre de problème arrive dans un
grand nombre de situations, et dans les systèmes de programmation (comme le C++) où
les objets doivent être explicitement détruits cela peut devenir relativement
complexe.
|
 |
 |
 |
With Java, the garbage collector is
designed to take care of the problem of releasing the memory (although this
doesn’t include other aspects of cleaning up an object). The garbage
collector “knows” when an object is no longer in use, and it then
automatically releases the memory for that object. This (combined with the fact
that all objects are inherited from the single root class Object and that
you can create objects only one way, on the heap) makes the process of
programming in Java much simpler than programming in C++. You have far fewer
decisions to make and hurdles to overcome.
|
 |
Avec Java, le ramasse-miettes est conçu pour s'occuper du
problème de la libération de la mémoire (bien que cela n'inclut pas les autres
aspects du nettoyage de l'objet). Le ramasse-miettes « sait » quand un objet
n'est plus utilisé, et il libère automatiquement la mémoire utilisée
par cet objet. Ceci (associé avec le fait que tous les objets sont dérivés de
la classe de base fondamentale Object et que les objets sont créés
dans le segment) rend la programmation Java plus simple que la programmation C++. Il y a beaucoup
moins de décisions à prendre et d'obstacles à surmonter.
|
 |
 |
 |
Garbage collectors vs. efficiency and flexibility
|
 |
Ramasse-miettes vs. efficacité et flexibilité
|
 |
 |
 |
If all this is such a good idea, why
didn’t they do the same thing in C++? Well of course there’s a price
you pay for all this programming convenience, and that price is run-time
overhead. As mentioned before, in C++ you can create objects on the stack, and
in this case they’re automatically cleaned up (but you don’t have
the flexibility of creating as many as you want at run-time). Creating objects
on the stack is the most efficient way to allocate storage for objects and to
free that storage. Creating objects on the heap can be much more expensive.
Always inheriting from a base class and making all function calls polymorphic
also exacts a small toll. But the garbage collector is a particular problem
because you never quite know when it’s going to start up or how long it
will take. This means that there’s an inconsistency in the rate of
execution of a Java program, so you can’t use it in certain situations,
such as when the rate of execution of a program is uniformly critical. (These
are generally called real time programs, although not all real time programming
problems are this stringent.)
|
 |
Si cette idée est si bonne, pourquoi le C++ n'intègre-t-il
pas ce mécanisme ? Bien sûr car il y a un prix à payer pour cette
facilité de programmation, et ce surcoût se traduit par du temps système. Comme
on l'a vu, en C++ on peut créer des objets dans la pile et dans ce cas ils sont
automatiquement nettoyés (mais dans ce cas on n'a pas la flexibilité de créer
autant d'objets que voulu lors de l'exécution). Créer des objets dans la pile est la
façon la plus efficace d'allouer de l'espace pour des objets et de libérer cet
espace. Créer des objets dans le segment est bien plus coûteux. Hériter de la
même classe de base et rendre tous les appels de fonctions polymorphes
prélèvent aussi un tribut. Mais le ramasse-miettes est un problème à
part car on ne sait pas quand il va démarrer ou combien de temps il va prendre. Cela veut
dire qu'il y a une inconsistance dans le temps d'exécution de programmes Java ; on ne
peut donc l'utiliser dans certaines situations, comme celles où le temps d'exécution
d'un programme est critique (appelés programmes en temps réel, bien que tous les
problèmes de programmation en temps réel ne soient pas aussi
astreignants).
|
 |
 |
 |
The designers of the C++ language, trying
to woo C programmers (and most successfully, at that), did not want to add any
features to the language that would impact the speed or the use of C++ in any
situation where programmers might otherwise choose C. This goal was realized,
but at the price of greater complexity when programming in C++. Java is simpler
than C++, but the trade-off is in efficiency and sometimes applicability. For a
significant portion of programming problems, however, Java is the superior
choice.
|
 |
Les concepteurs du langage C++, en voulant amadouer les programmeurs C, ne
voulurent pas ajouter de nouvelles fonctionnalités au langage qui puissent impacter la
vitesse ou défavoriser l'utilisation du C++ dans des situations où le C se serait
révélé acceptable. Cet objectif a été atteint, mais au prix
d'une plus grande complexité lorsqu'on programme en C++. Java est plus simple que le C++,
mais la contrepartie en est l'efficacité et quelquefois son champ d'applications. Pour un
grand nombre de problèmes de programmation cependant, Java constitue le meilleur
choix.
|
 |
 |
 |
Exception handling: dealing with errors
|
 |
Traitement des exceptions : gérer les erreurs
|
 |
 |
 |
Ever since the beginning of programming
languages, error handling has been one of the most difficult issues. Because
it’s so hard to design a good error handling scheme, many languages simply
ignore the issue, passing the problem on to library designers who come up with
halfway measures that can work in many situations but can easily be
circumvented, generally by just ignoring them. A major problem with most error
handling schemes is that they rely on programmer vigilance in following an
agreed-upon convention that is not enforced by the language. If the programmer
is not vigilant—often the case if they are in a hurry—these schemes
can easily be forgotten.
|
 |
Depuis les débuts des langages de programmation, le traitement des
erreurs s'est révélé l'un des problèmes les plus ardus. Parce qu'il est
difficile de concevoir un bon mécanisme de gestion des erreurs, beaucoup de langages
ignorent ce problème et le déléguent aux concepteurs de bibliothèques
qui fournissent des mécanismes qui fonctionnent dans beaucoup de situations mais peuvent
être facilement contournés, généralement en les ignorant. L'une des
faiblesses de la plupart des mécanismes d'erreur est qu'ils reposent sur la vigilance du
programmeur à suivre des conventions non imposées par le langage. Si le programmeur
n'est pas assez vigilant - ce qui est souvent le cas s'il est pressé - ces mécanismes
peuvent facilement être oubliés.
|
 |
 |
 |
Exception handling wires error handling
directly into the programming language and sometimes even the operating system.
An exception is an object that is “thrown” from the site of the
error and can be “caught” by an appropriate exception handler
designed to handle that particular type of error. It’s as if exception
handling is a different, parallel path of execution that can be taken when
things go wrong. And because it uses a separate execution path, it doesn’t
need to interfere with your normally executing code. This makes that code
simpler to write since you aren’t constantly forced to check for errors.
In addition, a thrown exception is unlike an error value that’s returned
from a function or a flag that’s set by a function in order to indicate an
error condition—these can be ignored. An exception cannot be ignored, so
it’s guaranteed to be dealt with at some point. Finally, exceptions
provide a way to reliably recover from a bad situation. Instead of just exiting
you are often able to set things right and restore the execution of a program,
which produces much more robust programs.
|
 |
Le système des exceptions pour gérer les erreurs se situe au
niveau du langage de programmation et parfois même au niveau du système
d'exploitation. Une exception est un objet qui est « émis » depuis
l'endroit où l'erreur est apparue et peut être intercepté par un gestionnaire
d'exception conçu pour gérer ce type particulier d'erreur. C'est comme si la gestion
des exceptions était un chemin d'exécution parallèle à suivre quand les
choses se gâtent. Et parce qu'elle utilise un chemin d'exécution séparé,
elle n'interfère pas avec le code s'exécutant normalement. Cela rend le code plus
simple à écrire car on n'a pas à vérifier constamment si des erreurs
sont survenues. De plus, une exception émise n'est pas comme une valeur de retour d'une
fonction signalant une erreur ou un drapeau positionné par une fonction pour indiquer une
erreur - ils peuvent être ignorés. Une exception ne peut pas être
ignorée, on a donc l'assurance qu'elle sera traitée quelque part. Enfin, les
exceptions permettent de revenir d'une mauvaise situation assez facilement. Plutôt que
terminer un programme, il est souvent possible de remettre les choses en place et de restaurer son
exécution, ce qui produit des programmes plus robustes.
|
 |
 |
 |
Java’s exception handling stands
out among programming languages, because in Java, exception handling was wired
in from the beginning and you’re forced to use it. If you don’t
write your code to properly handle exceptions, you’ll get a compile-time
error message. This guaranteed consistency makes error handling much
easier.
|
 |
Le traitement des exceptions de Java se distingue parmi les langages de
programmes, car en Java le traitement des exceptions a été intégré
depuis le début et on est forcé de l'utiliser. Si le code produit ne gère pas
correctement les exceptions, le compilateur générera des messages d'erreur. Cette
consistance rend la gestion des erreurs bien plus aisée.
|
 |
 |
 |
It’s worth noting that exception
handling isn’t an object-oriented feature, although in object-oriented
languages the exception is normally represented with an object. Exception
handling existed before object-oriented
languages.
|
 |
Il est bon de noter que le traitement des exceptions n'est pas une
caractéristique orientée objet, bien que dans les langages OO une exception soit
normalement représentée par un objet. Le traitement des exceptions existait avant les
langages orientés objet.
|
 |
 |
 |
Multithreading
|
 |
Multithreading
|
 |
 |
 |
A fundamental concept in computer
programming is the idea of handling more than one task at a time. Many
programming problems require that the program be able to stop what it’s
doing, deal with some other problem, and then return to the main process. The
solution has been approached in many ways. Initially, programmers with low-level
knowledge of the machine wrote interrupt service routines and the suspension of
the main process was initiated through a hardware interrupt. Although this
worked well, it was difficult and nonportable, so it made moving a program to a
new type of machine slow and expensive.
|
 |
L'un des concepts fondamentaux dans la programmation des ordinateurs est
l'idée de traiter plus d'une tâche à la fois. Beaucoup de problèmes
requièrent que le programme soit capable de stopper ce qu'il est en train de faire, traite
un autre problème puis retourne à sa tâche principale. Le problème a
été abordé de beaucoup de manières différentes. Au début,
les programmeurs ayant des connaissances sur le fonctionnement de bas niveau de la machine
écrivaient des routines d'interruption de service et l'interruption de la tâche
principale était initiée par une interruption matérielle. Bien que cela
fonctionne correctement, c'était difficile et non portable, et cela rendait le portage d'un
programme sur un nouveau type de machine lent et cher.
|
 |
 |
 |
Sometimes interrupts are necessary for
handling time-critical tasks, but there’s a large class of problems in
which you’re simply trying to partition the problem into separately
running pieces so that the whole program can be more responsive. Within a
program, these separately running pieces are called threads, and the general
concept is called multithreading. A common example of multithreading is
the user interface. By using threads, a user can press a button and get a quick
response rather than being forced to wait until the program finishes its current
task.
|
 |
Quelquefois les interruptions sont nécessaires pour gérer les
tâches critiques, mais il existe une large classe de problèmes dans lesquels on tente
juste de partitionner le problème en parties séparées et indépendantes
afin que le programme soit plus réactif. Dans un programme, ces parties
séparées sont appelés threads et le concept général est
appelé multithreading. Un exemple classique de multithreading est l'interface
utilisateur. En utilisant les threads, un utilisateur peur appuyer sur un bouton et obtenir une
réponse plus rapide que s'il devait attendre que le programme termine sa tâche
courante.
|
 |
 |
 |
Ordinarily, threads are just a way to
allocate the time of a single processor. But if the operating system supports
multiple processors, each thread can be assigned to a different processor and
they can truly run in parallel. One of the convenient features of multithreading
at the language level is that the programmer doesn’t need to worry about
whether there are many processors or just one. The program is logically divided
into threads and if the machine has more than one processor then the program
runs faster, without any special adjustments.
|
 |
Généralement, les threads sont juste une façon
d'allouer le temps d'un seul processeur. Mais si le système d'exploitation supporte les
multi-processeurs, chaque thread peut être assigné à un processeur
différent et ils peuvent réellement s'exécuter en parallèle. L'une des
caractéristiques intéressantes du multithreading au niveau du langage est que le
programmeur n'a pas besoin de se préoccuper du nombre de processeurs. Le programme est
divisé logiquement en threads et si la machine dispose de plusieurs processeurs, le
programme tourne plus vite, sans aucun ajustement.
|
 |
 |
 |
All this makes threading sound pretty
simple. There is a catch: shared resources. If you have more than one thread
running that’s expecting to access the same resource you have a problem.
For example, two processes can’t simultaneously send information to a
printer. To solve the problem, resources that can be shared, such as the
printer, must be locked while they are being used. So a thread locks a resource,
completes its task, and then releases the lock so that someone else can use the
resource.
|
 |
Tout ceci pourrait faire croire que le multithreading est simple. Il y a
toutefois un point d'achoppement : les ressources partagées. Un problème se pose
si plus d'un thread s'exécutant veulent accéder à la même ressource. Par
exemple, deux tâches ne peuvent envoyer simultanément de l'information à une
imprimante. Pour résoudre ce problème, les ressources pouvant être
partagées, comme l'imprimante, doivent être verrouillées avant d'être
utilisées. Un thread verrouille donc une ressource, accomplit ce qu'il a à faire, et
ensuite relâche le verrou posé afin que quelqu'un d'autre puisse utiliser cette
ressource.
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |