 |
 |
6) Réutiliser les classes |
|
 |
|
Texte original |
 |
Traducteur : Olivier THOMANN |
|
 |
///
|
Ce chapitre contient 4 pages
1
2
3
4
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
Catching base constructor
exceptions
|
 |
Attraper les exceptions du constructeur de base.
|
 |
 |
 |
As just noted, the compiler forces you to
place the base-class constructor call first in the body of the derived-class
constructor. This means nothing else can appear before it. As you’ll see
in Chapter 10, this also prevents a derived-class constructor from catching any
exceptions that come from a base class. This can be inconvenient at
times.
|
 |
Comme nous venons de le préciser, le compilateur nous force à placer
l'appel du constructeur de la classe de base en premier dans le constructeur de la classe dérivée.
Cela signifie que rien ne peut être placé avant lui. Comme vous le verrez dans le chapitre 10, cela
empêche également le constructeur de la classe dérivée d'attraper une exception qui provient de la
classe de base. Ceci peut être un inconvénient de temps en temps.
|
 |
 |
 |
Combining composition and
inheritance
|
 |
Combiner composition et héritage.
|
 |
 |
 |
It is very common to use composition and
inheritance together. The following example shows the creation of a more complex
class, using both inheritance and composition, along with the necessary
constructor
initialization:
|
 |
Il est très classique d'utiliser ensemble la composition et l'héritage.
L'exemple suivant montre la création d'une classe plus complexe, utilisant à la fois l'héritage et
la composition, avec la nécessaire initialisation des contructeurs:
|
 |
 |
 |
//: c06:PlaceSetting.java // Combining composition & inheritance.
class Plate { Plate(int i) { System.out.println("Plate constructor"); } }
class DinnerPlate extends Plate { DinnerPlate(int i) { super(i); System.out.println( "DinnerPlate constructor"); } }
class Utensil { Utensil(int i) { System.out.println("Utensil constructor"); } }
class Spoon extends Utensil { Spoon(int i) { super(i); System.out.println("Spoon constructor"); } }
class Fork extends Utensil { Fork(int i) { super(i); System.out.println("Fork constructor"); } }
class Knife extends Utensil { Knife(int i) { super(i); System.out.println("Knife constructor"); } }
// A cultural way of doing something: class Custom { Custom(int i) { System.out.println("Custom constructor"); } }
public class PlaceSetting extends Custom { Spoon sp; Fork frk; Knife kn; DinnerPlate pl; PlaceSetting(int i) { super(i + 1); sp = new Spoon(i + 2); frk = new Fork(i + 3); kn = new Knife(i + 4); pl = new DinnerPlate(i + 5); System.out.println( "PlaceSetting constructor"); } public static void main(String[] args) { PlaceSetting x = new PlaceSetting(9); } } ///:~
|
 |
// ! c06:PlaceSetting.java // Mélanger composition & héritage.
class Plate { Plate(int i) { System.out.println("Plate constructor"); } }
class DinnerPlate extends Plate { DinnerPlate(int i) { super(i); System.out.println( "DinnerPlate constructor"); } }
class Utensil { Utensil(int i) { System.out.println("Utensil constructor"); } }
class Spoon extends Utensil { Spoon(int i) { super(i); System.out.println("Spoon constructor"); } }
class Fork extends Utensil { Fork(int i) { super(i); System.out.println("Fork constructor"); } }
class Knife extends Utensil { Knife(int i) { super(i); System.out.println("Knife constructor"); } }
// Une manière culturelle de faire quelque chose: class Custom { Custom(int i) { System.out.println("Custom constructor"); } }
public class PlaceSetting extends Custom { Spoon sp; Fork frk; Knife kn; DinnerPlate pl; PlaceSetting(int i) { super(i + 1); sp = new Spoon(i + 2); frk = new Fork(i + 3); kn = new Knife(i + 4); pl = new DinnerPlate(i + 5); System.out.println( "PlaceSetting constructor"); } public static void main(String[] args) { PlaceSetting x = new PlaceSetting(9); } } ///:~
|
 |
 |
 |
While the compiler forces you to
initialize the base classes, and requires that you do it right at the beginning
of the constructor, it doesn’t watch over you to make sure that you
initialize the member objects, so you must remember to pay attention to
that.
|
 |
Tant que le compilateur nous force à initialiser les classes de base, et
requiert que nous le fassions directement au début du constructeur, il ne vérifie pas que nous
initialisons les objets membres, donc nous devons nous souvenir de faire attention à
cela.
|
 |
 |
 |
Guaranteeing proper cleanup
|
 |
Garantir un nettoyage propre
|
 |
 |
 |
Java doesn’t have the C++ concept
of a destructor, a method that is automatically
called when an object is destroyed. The reason is probably that in Java the
practice is simply to forget about objects rather than to destroy them, allowing
the garbage collector to reclaim
the memory as necessary.
|
 |
Java ne possède pas le concept C++ de destructeur, une méthode qui
est automatiquement appelée quand un objet est détruit. La raison est probablement qu'en Java la
pratique est simplement d'oublier les objets plutôt que les détruire, laissant le ramasse-miette réclamer la mémoire selon les besoins.
|
 |
 |
 |
Often this is fine, but there are times
when your class might perform some activities during its lifetime that require
cleanup. As mentioned in Chapter 4, you can’t know when the garbage
collector will be called, or if it will be called. So if you want something
cleaned up for a class, you must explicitly write a special method to do it, and
make sure that the client programmer knows that they must call this method. On
top of this—as described in Chapter 10
(“Error Handling with Exceptions”)—you
must guard against an exception by putting such cleanup in a
finally clause.
|
 |
Souvent cela convient, mais il existe des cas où votre classe pourrait,
durant son existence, exécuter des tâches nécessitant un nettoyage. Comme mentionné dans le
chapitre 4, on ne peut pas savoir quand le ramasse-miettes sera exécuté, ou s'il sera appelé. Donc
si on veut nettoyer quelque chose pour une classe, on doit écrire une méthode particulière, et être
sûr que l'usager sait qu'il doit appeler cette méthode. Par dessus tout, comme décrit dans le
chapitre 10 (« Gestion d'erreurs avec les exceptions ») - on doit se protéger contre une
exception en mettant un tel nettoyage dans une clause finally.
|
 |
 |
 |
Consider an example of a computer-aided
design system that draws pictures on the screen:
|
 |
Considérons un exemple d'un système de conception assisté par ordinateur
qui dessine des images sur l'écran:
|
 |
 |
 |
//: c06:CADSystem.java // Ensuring proper cleanup. import java.util.*;
class Shape { Shape(int i) { System.out.println("Shape constructor"); } void cleanup() { System.out.println("Shape cleanup"); } }
class Circle extends Shape { Circle(int i) { super(i); System.out.println("Drawing a Circle"); } void cleanup() { System.out.println("Erasing a Circle"); super.cleanup(); } }
class Triangle extends Shape { Triangle(int i) { super(i); System.out.println("Drawing a Triangle"); } void cleanup() { System.out.println("Erasing a Triangle"); super.cleanup(); } }
class Line extends Shape { private int start, end; Line(int start, int end) { super(start); this.start = start; this.end = end; System.out.println("Drawing a Line: " + start + ", " + end); } void cleanup() { System.out.println("Erasing a Line: " + start + ", " + end); super.cleanup(); } }
public class CADSystem extends Shape { private Circle c; private Triangle t; private Line[] lines = new Line[10]; CADSystem(int i) { super(i + 1); for(int j = 0; j < 10; j++) lines[j] = new Line(j, j*j); c = new Circle(1); t = new Triangle(1); System.out.println("Combined constructor"); } void cleanup() { System.out.println("CADSystem.cleanup()"); // The order of cleanup is the reverse // of the order of initialization t.cleanup(); c.cleanup(); for(int i = lines.length - 1; i >= 0; i--) lines[i].cleanup(); super.cleanup(); } public static void main(String[] args) { CADSystem x = new CADSystem(47); try { // Code and exception handling... } finally { x.cleanup(); } } } ///:~
|
 |
// ! c06:CADSystem.java // Assurer un nettoyage propre. import java.util.*;
class Shape { Shape(int i) { System.out.println("Shape constructor"); } void cleanup() { System.out.println("Shape cleanup"); } }
class Circle extends Shape { Circle(int i) { super(i); System.out.println("Drawing a Circle"); } void cleanup() { System.out.println("Erasing a Circle"); super.cleanup(); } }
class Triangle extends Shape { Triangle(int i) { super(i); System.out.println("Drawing a Triangle"); } void cleanup() { System.out.println("Erasing a Triangle"); super.cleanup(); } }
class Line extends Shape { private int start, end; Line(int start, int end) { super(start); this.start = start; this.end = end; System.out.println("Drawing a Line : " + start + ", " + end); } void cleanup() { System.out.println("Erasing a Line : " + start + ", " + end); super.cleanup(); } }
public class CADSystem extends Shape { private Circle c; private Triangle t; private Line[] lines = new Line[10]; CADSystem(int i) { super(i + 1); for(int j = 0; j < 10; j++) lines[j] = new Line(j, j*j); c = new Circle(1); t = new Triangle(1); System.out.println("Combined constructor"); } void cleanup() { System.out.println("CADSystem.cleanup()"); // L'ordre de nettoyage est l'inverse // de l'ordre d'initialisation t.cleanup(); c.cleanup(); for(int i = lines.length - 1; i >= 0; i--) lines[i].cleanup(); super.cleanup(); } public static void main(String[] args) { CADSystem x = new CADSystem(47); try { // Code et gestion des exceptions... } finally { x.cleanup(); } } } ///:~
|
 |
 |
 |
Everything in this system is some kind of
Shape (which is itself a kind of Object since it’s
implicitly inherited from the root class). Each class redefines
Shape’s cleanup( ) method in addition to calling the
base-class version of that method using super. The specific Shape
classes—Circle, Triangle and Line—all have
constructors that “draw,” although any method called during the
lifetime of the object could be responsible for doing something that needs
cleanup. Each class has its own cleanup( ) method to restore
nonmemory things back to the way they were before the object
existed.
|
 |
Tout dans ce système est une sorte de Shape (lequel est
une sorte d'Object puisqu'il hérite implicitement de la classe racine). Chaque
classe redéfinit la méthode cleanup( ) de Shape en plus
d'appeler la méthode de la classe de base en utilisant super. Les classes
Shape spécifiques —Circle, Triangle et
Line— ont toutes des constructeurs qui « dessinent », bien que
n'importe quelle méthode appelée durant la durée de vie d'un objet pourrait être responsable de
faire quelque chose qui nécessite un nettoyage. Chaque classe possède sa propre méthode
cleanup( ) pour restaurer les choses de la manière dont elles étaient avant
que l'objet n'existe.
|
 |
 |
 |
In main( ), you can see two
keywords that are new, and won’t officially be introduced until Chapter
10: try and finally.
The try keyword indicates that the block that follows (delimited by curly
braces) is a guarded region, which means that it is given special
treatment. One of these special treatments is that the code in the
finally clause following this guarded region is always executed,
no matter how the try block exits. (With exception handling, it’s
possible to leave a try block in a number of nonordinary ways.) Here, the
finally clause is saying “always call cleanup( ) for
x, no matter what happens.” These keywords will be explained
thoroughly in Chapter 10.
|
 |
main( ), on peut noter deux nouveaux mot-clés qui ne
seront pas officiellement introduits avant le chapitre 10 : try et
finally. Le mot-clé try indique que le bloc qui suit (délimité
par les accolades) est une région gardée, ce qui signifie qu'elle a un traitement
particulier. Un de ces traitements particuliers est que le code dans la clause
finally suivant la région gardée est toujours exécuté, quelle que soit la
manière dont le bloc try termine. Avec la gestion des exceptions, il est possible
de quitter le bloc try de manière non ordinaire. Ici la clause finally dit de
toujours appeler cleanup( ) pour x, quoiqu'il arrive. Ces mot-clés seront
expliqués plus en profondeur dans le chapitre 10.
|
 |
 |
 |
Note that in your cleanup method you must
also pay attention to the calling order for the base-class and member-object
cleanup methods in case one subobject depends on another. In general, you should
follow the same form that is imposed by a C++ compiler on its destructors: First
perform all of the cleanup work specific to your class, in the reverse order of
creation. (In general, this requires that base-class elements still be viable.)
Then call the base-class cleanup method, as demonstrated here.
|
 |
Notez que dans votre méthode cleanup vous devez faire attention à l'ordre
d'appel pour les méthodes cleanup de la classe de base et celle des objet-membres au cas où un des
sous-objets dépend des autres. En général, vous devriez suivre la même forme qui est imposée pour
le compilateur C++ sur ses destructeurs : premièrement exécuter tout le nettoyage
spécifique à votre classe, dans l'ordre inverse de la création. En général, cela nécessite que les
éléments de la classe de base soient encore viable. Ensuite appeler la méthode cleanup de la classe
de base, comme démontré ici.
|
 |
 |
 |
There can be many cases in which the
cleanup issue is not a problem; you just let the garbage collector do the work.
But when you must do it explicitly, diligence and attention is
required.
|
 |
Il y a beaucoup de cas pour lesquels le problème de nettoyage n'est pas un
problème ; on laisse le ramasse-miettes faire le travail. Mais quand on doit le faire
explicitement, diligence et attention sont requis.
|
 |
 |
 |
Order of garbage collection
|
 |
L'ordre du ramasse-miettes
|
 |
 |
 |
There’s not much you can rely on
when it comes to garbage collection. The garbage
collector might never be called. If it is, it can reclaim objects in any order
it wants. It’s best to not rely on garbage collection for anything but
memory reclamation. If you want cleanup to take place, make your own cleanup
methods and don’t rely on finalize( ).
(As mentioned in Chapter 4, Java can be forced to call all the
finalizers.)
|
 |
Il n'y a pas grand chose sur quoi on puisse se fier quand il s'agit de
ramasse-miettes. Le ramasse-miette peut ne jamais être appelé. S'il l'est, il peut réclamer les
objets dans n'importe quel ordre. Il est préférable de ne pas se fier au ramasse-miette pour autre
chose que libérer la mémoire. Si on veut que le nettoyage ait lieu, faites vos propre méthodes de
nettoyage et ne vous fiez pas à finalize( ). Comme mentionné dans le chapitre
4, Java peut être forcé d'appeler tous les finalizers.
|
 |
 |
 |
Name hiding
|
 |
Cacher les noms
|
 |
 |
 |
Only C++ programmers might be surprised
by name hiding, since it works differently in that language.
If
a Java base class has a method name that’s overloaded several times,
redefining that method name in the derived class will not hide any of the
base-class versions. Thus overloading works regardless of whether the method was
defined at this level or in a base class:
|
 |
Seuls les programmeurs C++ pourraient être surpris par le masquage de noms,
étant donné que le fonctionnement est différent dans ce langage. Si une
classe de base Java a un nom de méthode qui est surchargé plusieurs fois, redéfinir ce nom de
méthode dans une sous-classe ne cachera aucune des versions de la classe de base. Donc la surcharge
fonctionne sans savoir si la méthode était définie à ce niveau ou dans une classe de
base:
|
 |
 |
 |
//: c06:Hide.java // Overloading a base-class method name // in a derived class does not hide the // base-class versions.
class Homer { char doh(char c) { System.out.println("doh(char)"); return 'd'; } float doh(float f) { System.out.println("doh(float)"); return 1.0f; } }
class Milhouse {}
class Bart extends Homer { void doh(Milhouse m) {} }
class Hide { public static void main(String[] args) { Bart b = new Bart(); b.doh(1); // doh(float) used b.doh('x'); b.doh(1.0f); b.doh(new Milhouse()); } } ///:~
|
 |
// ! c06:Hide.java // Surchage le nom d'une méthode de la classe de base // dans une classe dérivée ne cache pas // les versions de la classe de base.
class Homer { char doh(char c) { System.out.println("doh(char)"); return 'd'; } float doh(float f) { System.out.println("doh(float)"); return 1.0f; } }
class Milhouse {}
class Bart extends Homer { void doh(Milhouse m) {} }
class Hide { public static void main(String[] args) { Bart b = new Bart(); b.doh(1); // doh(float) utilisé b.doh('x'); b.doh(1.0f); b.doh(new Milhouse()); } } ///:~
|
 |
 |
 |
As you’ll see in the next chapter,
it’s far more common to override methods of the same name using exactly
the same signature and return type as in the base class. It can be confusing
otherwise (which is why C++ disallows it, to prevent you from making what is
probably a
mistake).
|
 |
Comme nous le verrons dans le prochain chapitre, il est beaucoup plus
courant de surcharger les méthodes de même nom et utilisant exactement la même signature et le type
retour que dans la classe de base. Sinon cela peut être source de confusion. C'est pourquoi le C++
ne le permet pas, pour empêcher de faire ce qui est probablement une erreur.
|
 |
 |
 |
Choosing composition vs.
inheritance
|
 |
Choisir la composition à la place de l'héritage
|
 |
 |
 |
Both composition and inheritance allow
you to place subobjects inside your new class. You might
wonder about the difference between the two, and when to choose one over the
other.
|
 |
La composition et l'héritage permettent tous les deux de placer des
sous-objets à l'intérieur de votre nouvelle classe. Vous devriez vous demander quelle est la
différence entre les deux et quand choisir l'une plutôt que l'autre.
|
 |
 |
 |
Composition is generally used when you
want the features of an existing class inside your new class, but not its
interface. That is, you embed an object so that you can use it to implement
functionality in your new class, but the user of your new class sees the
interface you’ve defined for the new class rather than the interface from
the embedded object. For this effect, you embed private objects of
existing classes inside your new class.
|
 |
La composition est généralement utilisée quand on a besoin des
caractéristiques d'une classe existante dans une nouvelle classe, mais pas son interface. On inclut
un objet donc on peut l'utiliser pour implémenter une fonctionnalité dans la nouvelle classe, mais
l'utilisateur de la nouvelle classe voit l'interface qu'on a défini et non celle de l'objet inclus.
Pour ce faire, il suffit d'inclure des objets private de classes existantes dans
la nouvelle classe.
|
 |
 |
 |
Sometimes it makes sense to allow the
class user to directly access the composition of your new class; that is, to
make the member objects public. The member objects use implementation
hiding themselves, so this is a safe thing to do. When the user knows
you’re assembling a bunch of parts, it makes the interface easier to
understand. A car object is a good example:
|
 |
Parfois il est sensé de permettre à l'utilisateur d'une classe d'accéder
directement à la composition de notre nouvelle classe ; pour ce faire on déclare les
objets membres public. Les objets membres utilisent l'implémentation en se cachant
les uns des autres, ce qui est une bonne chose. Quand l'utilisateur sait qu'on assemble un ensemble
de parties, cela rend l'interface plus facile à comprendre. Un objet car (voiture
en anglais) est un bon exemple:
|
 |
 |
 |
//: c06:Car.java // Composition with public objects.
class Engine { public void start() {} public void rev() {} public void stop() {} }
class Wheel { public void inflate(int psi) {} }
class Window { public void rollup() {} public void rolldown() {} }
class Door { public Window window = new Window(); public void open() {} public void close() {} }
public class Car { public Engine engine = new Engine(); public Wheel[] wheel = new Wheel[4]; public Door left = new Door(), right = new Door(); // 2-door public Car() { for(int i = 0; i < 4; i++) wheel[i] = new Wheel(); } public static void main(String[] args) { Car car = new Car(); car.left.window.rollup(); car.wheel[0].inflate(72); } } ///:~
|
 |
// ! c06:Car.java // Composition avec des objets publics.
class Engine { public void start() {} public void rev() {} public void stop() {} }
class Wheel { public void inflate(int psi) {} }
class Window { public void rollup() {} public void rolldown() {} }
class Door { public Window window = new Window(); public void open() {} public void close() {} }
public class Car { public Engine engine = new Engine(); public Wheel[] wheel = new Wheel[4]; public Door left = new Door(), right = new Door(); // 2-door public Car() { for(int i = 0; i < 4; i++) wheel[i] = new Wheel(); } public static void main(String[] args) { Car car = new Car(); car.left.window.rollup(); car.wheel[0].inflate(72); } } ///:~
|
 |
 |
 |
Because the composition of a car is part
of the analysis of the problem (and not simply part of the underlying design),
making the members public assists the client programmer’s
understanding of how to use the class and requires less code complexity for the
creator of the class. However, keep in mind that this is a special case and that
in general you should make fields private.
|
 |
Du fait que la composition de la voiture fait partie de l'analyse du
problème (et non pas simplement de la conception sous-jacente), rendre les membre
publics aide le programmeur client à comprendre comment utiliser la classe et
nécessite moins de complexité de code pour le créateur de la classe. Quoiqu'il en soit, gardez à
l'esprit que c'est un cas spécial et qu'en général on devrait définir les champs
privés.
|
 |
 |
 |
When
you inherit, you take an existing class and make a special version of it. In
general, this means that you’re taking a general-purpose class and
specializing it for a particular need. With a little thought, you’ll see
that it would make no sense to compose a car using a vehicle object—a car
doesn’t contain a vehicle, it is a vehicle. The
is-a relationship is expressed with inheritance,
and the has-a relationship is expressed with
composition.
|
 |
Quand on hérite, on prend la classe existante et on en fait une version
spéciale. En général, cela signifie qu'on prend une classe d'usage général et on l'adapte à un cas
particulier. Avec un peu de bon sens, vous verrez que ça n'a pas de sens de composer une voiture en
utilisant un objet véhicule. Une voiture ne contient pas un véhicule, c'est un véhicule.
La relation est-un s'exprime avec l'héritage et la relation a-un s'exprime avec
la composition.
|
 |
 |
 |
protected
|
 |
protected
|
 |
 |
 |
Now that you’ve been introduced to
inheritance, the keyword protected finally has
meaning. In an ideal world, private members would always be hard-and-fast
private, but in real projects there are times when you want to make
something hidden from the world at large and yet allow access for members of
derived classes. The protected keyword is a nod to pragmatism. It says
“This is private as far as the class user is concerned, but
available to anyone who inherits from this class or anyone else in the same
package.” That is,
protected in Java is
automatically “friendly.”
|
 |
Maintenant que nous avons introduit l'héritage, le mot clé
protected prend finalement un sens. Dans un monde idéal, les membres
private devraient toujours être des membres private purs et durs,
mais dans les projets réels il arrive souvent qu'on veuille cacher quelque chose au monde au sens
large et qu'on veuille permettre l'accès pour les membres des classes dérivées. Le mot clé
protected est un moyen pragmatique de faire. Il dit « Ceci est
private en ce qui concerne la classe utilisatrice, mais c'est disponible pour
quiconque hérite de cette classe ou appartient au même package ». C'est pourquoi
protected en Java est automatiquement « friendly ».
|
 |
 |
 |
The best tack to take is to leave the
data members private—you should always
preserve your right to change the underlying implementation. You can then allow
controlled access to inheritors of your class through
protected methods:
|
 |
La meilleure approche est de laisser les membres de données
private. Vous devriez toujours préserver le droit de changer l'implémentation sous
jacente. Ensuite vous pouvez permettre l'accès contrôlé pour les héritiers de votre classe à
travers les méthodes protected :
|
 |
 |
 |
//: c06:Orc.java // The protected keyword. import java.util.*;
class Villain { private int i; protected int read() { return i; } protected void set(int ii) { i = ii; } public Villain(int ii) { i = ii; } public int value(int m) { return m*i; } }
public class Orc extends Villain { private int j; public Orc(int jj) { super(jj); j = jj; } public void change(int x) { set(x); } } ///:~
|
 |
// ! c06:Orc.java // Le mot clé protected . import java.util.*;
class Villain { private int i; protected int read() { return i; } protected void set(int ii) { i = ii; } public Villain(int ii) { i = ii; } public int value(int m) { return m*i; } }
public class Orc extends Villain { private int j; public Orc(int jj) { super(jj); j = jj; } public void change(int x) { set(x); } } ///:~
|
 |
 |
 |
You can see that change( )
has access to set( ) because it’s
protected.
|
 |
On peut voir que change( ) a accès à
set( ) parce qu'il est protected.
|
 |
 |
 |
Incremental development
|
 |
Développement incrémental
|
 |
 |
 |
One of the advantages of inheritance is
that it supports incremental
development by allowing you to
introduce new code without causing bugs in existing code. This also isolates new
bugs inside the new code. By inheriting from an existing, functional class and
adding data members and methods (and redefining existing methods), you leave the
existing code—that someone else might still be using—untouched and
unbugged. If a bug happens, you know that it’s in your new code, which is
much shorter and easier to read than if you had modified the body of existing
code.
|
 |
Un des avantages de l'héritage est qu'il supporte le développement
incrémental en permettant d'ajouter du code sans créer de bogues dans le code existant. Ceci
permet également d'isoler les nouveaux bogues dans le nouveau code. En héritant d'une classe
existante et fonctionnelle et en ajoutant des données membres et des méthodes et en redéfinissant
des méthodes existantes, on laisse le code existant - que quelqu'un d'autre peut encore utiliser -
inchangé et non bogué. Si un bogue survient, on sait alors qu'il est dans le nouveau code, lequel
est beaucoup plus rapide et facile à lire que si on avait modifié le code existant.
|
 |
 |
 |
It’s rather amazing how cleanly the
classes are separated. You don’t even need the source code for the methods
in order to reuse the code. At most, you just import a package. (This is true
for both inheritance and composition.)
|
 |
Il est plutôt surprenant de voir que les classes sont séparées proprement.
Nous n'avons même pas besoin du code source des méthodes afin de pouvoir les utiliser. Au pire, on
importe simplement un package. Ceci est vrai à la fois pour l'héritage et la
composition.
|
 |
 |
 |
It’s important to realize that
program development is an incremental process, just like human learning. You can
do as much analysis as you want, but you still won’t know all the answers
when you set out on a project. You’ll have much more success—and
more immediate feedback—if you start out to “grow” your
project as an organic, evolutionary creature, rather than constructing it all at
once like a glass-box skyscraper.
|
 |
Il est important de réaliser que le développement d'un programme est un
processus incrémental, exactement comme l'apprentissage humain. On peut analyser autant que l'on
veut, mais on ne connaîtra pas toutes les réponses au démarrage d'un projet. Vous aurez beaucoup
plus de succès et de retour immédiat si vous commencez par faire « grandir » votre projet
comme un organisme organique et évolutif plutôt que de le construire comme un gratte-ciel en
verre.
|
 |
 |
 |
Although inheritance for experimentation
can be a useful technique, at some point after things stabilize you need to take
a new look at your class hierarchy with an eye to collapsing it into a sensible
structure. Remember that underneath it all, inheritance is meant to express a
relationship that says “This new class is a type of that old
class.” Your program should not be concerned with pushing bits around, but
instead with creating and manipulating objects of various types to express a
model in the terms that come from the problem
space.
|
 |
Bien que l'héritage pour l'expérimentation puisse être une technique utile,
à un moment donné les choses se stabilisent et vous devez jeter un regard neuf à votre hiérarchie
de classes pour la réduire en une structure plus logique. Souvenons nous qu'en dessous de tout,
l'héritage est utilisé pour exprimer une relation qui dit : « Cette nouvelle classe
est du type de l'ancienne classe ». Votre programme ne devrait pas être concerné par
la manipulation de bits ici ou là, mais par la création et la manipulation d'objets de différents
types afin d'exprimer un modèle dans les termes propres à l'espace du problème.
|
 |
 |
 |
Upcasting
|
 |
Transtypage ascendant
|
 |
 |
 |
The most important aspect of inheritance
is not that it provides methods for the new class. It’s the relationship
expressed between the new class and the base class. This
relationship can be summarized by saying “The new
class is a type of the existing class.”
|
 |
L'aspect le plus important de l'héritage n'est pas qu'il fournisse des
méthodes pour les nouvelles classes. C'est la relation exprimée entre la nouvelle classe et la
classe de base. Cette relation peut être résumée en disant : « La nouvelle classe
est un type de la classe existante ».
|
 |
 |
 |
This description is not just a fanciful
way of explaining inheritance—it’s supported directly by the
language. As an example, consider a base class called Instrument that
represents musical instruments, and a derived class called Wind. Because
inheritance means that all of the methods in the base class are also available
in the derived class, any message you can send to the base class can also be
sent to the derived class. If the Instrument class has a
play( ) method, so will Wind instruments. This means we can
accurately say that a Wind object is also a type of Instrument.
The following example shows how the compiler supports this
notion:
|
 |
Cette description n'est pas simplement une manière amusante d'expliquer
l'héritage - c'est supporté directement par le langage. Comme exemple, considérons une classe de
base appelée Instrument qui représente les instruments de musique et une classe
dérivée appelée Wind. Puisque l'héritage signifie que toutes les méthodes de la
classe de base sont également disponibles pour la classe dérivée, n'importe quel message envoyé à
la classe de base peut également être envoyé à la classe dérivée. Si la classe
Instrument a une méthode play( ), les instruments
Wind également. Cela signifie qu'il est exact de dire qu'un objet
Wind est également un type de Instrument. L'exemple suivant
montre comment le compilateur implémente cette notion :
|
 |
 |
 |
//: c06:Wind.java // Inheritance & upcasting. import java.util.*;
class Instrument { public void play() {} static void tune(Instrument i) { // ... i.play(); } }
// Wind objects are instruments // because they have the same interface: class Wind extends Instrument { public static void main(String[] args) { Wind flute = new Wind(); Instrument.tune(flute); // Upcasting } } ///:~
|
 |
// ! c06:Wind.java // Héritage & transtypage ascendant. import java.util.*;
class Instrument { public void play() {} static void tune(Instrument i) { // ... i.play(); } }
// Les objets Wind sont des instruments // parce qu'ils ont la même interface: class Wind extends Instrument { public static void main(String[] args) { Wind flute = new Wind(); Instrument.tune(flute); // Transtypage ascendant } } ///:~
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |