|
|
|
C) Conseils pour une programation stylée en java |
|
|
|
Texte original |
|
Traducteur :
Jérome Quelin |
|
|
|
Ce chapitre contient
2 pages
1
2
|
|
|
|
|
|
|
|
|
|
|
05.07.01 - version 5.6 : - Ajout des tags de séparation de pages pour le site (armel). 24.04.2001 - version 5.5 - Nettoyage du code Html [Arme]. 26.06.2000 - version 5.4 - Insertion du journal de log. 21.06.2000 - version 5.3 - Corrections apportées par Jean-Pierre Vidal. 18.06.2000 - version 5.2 - Modification des tags <url: http://www.blahblah.com> en www.blahblah.com. - Modification des guillemets "" en «». 10.06.2000 - version 5.1 - Première publication sur eGroups. Traducteur : - Jérome QUELIN Texte original : -Thinking in Java, 2nd edition, Revision 10 © 2000 by Bruce Eckel
|
|
|
|
C: Java Programming Guidelines
|
|
C : Conseils pour une programmation stylée en Java
|
|
|
|
This appendix contains suggestions
to help guide you in performing low-level program design, and in writing
code.
|
|
Cette annexe contient des conseils destinés à vous aider à
structurer vos programmes et écrire le code source de ceux-ci.
|
|
|
|
Naturally, these are guidelines and not
rules. The idea is to use them as inspirations, and to remember that there are
occasional situations where you need to bend or break a
rule.
|
|
Bien entendu, ce ne sont que des suggestions, en aucun cas des règles à
suivre à la lettre. L'idée ici est de s'en inspirer, tout en se rappelant que certaines situations
imposent de faire une entorse aux règles.
|
|
|
|
Design
|
|
Conception
|
|
|
|
- Elegance always
pays off. In the short term it might seem like it takes much longer to come
up with a truly graceful solution to a problem, but when it works the first time
and easily adapts to new situations instead of requiring hours, days, or months
of struggle, you’ll see the rewards (even if no one can measure them). Not
only does it give you a program that’s easier to build and debug, but
it’s also easier to understand and maintain, and that’s where the
financial value lies. This point can take some experience to understand, because
it can appear that you’re not being productive while you’re making a
piece of code elegant. Resist the urge to hurry; it will only slow you
down.
- First
make it work, then make it fast. This is true even if you are certain that a
piece of code is really important and that it will be a principal bottleneck in
your system. Don’t do it. Get the system going first with as simple a
design as possible. Then if it isn’t going fast enough, profile it.
You’ll almost always discover that “your” bottleneck
isn’t the problem. Save your time for the really important
stuff.
- Remember
the “divide and conquer” principle. If the problem you’re
looking at is too confusing, try to imagine what the basic operation of the
program would be, given the existence of a magic “piece” that
handles the hard parts. That “piece” is an object—write the
code that uses the object, then look at the object and encapsulate its
hard parts into other objects,
etc.
- Separate
the class creator from the class user (client programmer). The class
user is the “customer” and doesn’t need or want to know
what’s going on behind the scenes of the class. The class creator must be
the expert in class design and write the class so that it can be used by the
most novice programmer possible, yet still work robustly in the application.
Library use will be easy only if it’s
transparent.
- When
you create a class, attempt to make your names so clear that comments are
unnecessary. Your goal should be to make the client programmer’s
interface conceptually simple. To this end, use method overloading when
appropriate to create an intuitive, easy-to-use
interface.
- Your
analysis and design must produce, at minimum, the classes in your system, their
public interfaces, and their relationships to other classes, especially base
classes. If your design methodology produces more than that, ask yourself if
all the pieces produced by that methodology have value over the lifetime of the
program. If they do not, maintaining them will cost you. Members of development
teams tend not to maintain anything that does not contribute to their
productivity; this is a fact of life that many design methods don’t
account
for.
- Automate
everything. Write the test code first (before you write the class),
and keep it with the class. Automate the running of your tests through a
makefile or similar tool. This way, any changes can be automatically verified by
running the test code, and you’ll immediately discover errors. Because you
know that you have the safety net of your test framework, you will be bolder
about making sweeping changes when you discover the need. Remember that the
greatest improvements in languages come from the built-in testing provided by
type checking, exception handling, etc., but those features take you only so
far. You must go the rest of the way in creating a robust system by filling in
the tests that verify features that are specific to your class or
program.
- Write
the test code first (before you write the class) in order to verify that your
class design is complete. If you can’t write test code, you
don’t know what your class looks like. In addition, the act of writing the
test code will often flush out additional features or constraints that you need
in the class—these features or constraints don’t always appear
during analysis and design. Tests also provide example code showing how your
class can be
used.
- All
software design problems can be simplified by introducing an extra level of
conceptual indirection. This fundamental rule of software
engineering[85]
is the basis of abstraction, the primary feature of object-oriented
programming.
- An
indirection should have a meaning (in concert with guideline 9). This
meaning can be something as simple as “putting commonly used code in a
single method.” If you add levels of indirection (abstraction,
encapsulation, etc.) that don’t have meaning, it can be as bad as not
having adequate
indirection.
- Make
classes as atomic as possible. Give each class a single, clear purpose. If
your classes or your system design grows too complicated, break complex classes
into simpler ones. The most obvious indicator of this is sheer size: if a class
is big, chances are it’s doing too much and should be broken
up.
|
|
- L'élégance est toujours récompensée. C'est vrai que cela
peut prendre un peu plus de temps pour arriver à une solution élégante du problème considéré, mais
les bénéfices en sont tout de suite apparents (même si personne ne peut les quantifier réellement)
lorsque le programme marche du premier coup et s'adapte facilement à de nouvelles situations plutôt
que de devoir se battre avec pendant des heures, des jours et des mois. Non seulement le programme
est plus facile à écrire et à débugguer, mais il est aussi plus facile à comprendre et à maintenir,
et c'est là que réside sa valeur financière. Ce point demande de l'expérience pour être
complètement assimilé, parce qu'on est tenté de se dire que la recherche d'un code élégant fait
baisser la productivité. Se précipiter ne peut que vous ralentir.
- D'abord le faire marcher, ensuite l'optimiser. Ceci est
vrai même si on est certain qu'une portion de code est réellement importante et sera un goulot
d'étranglement pour le système. D'abord faire fonctionner le système avec une conception aussi
simple que possible. Ensuite le profiler s'il n'est pas assez rapide : on se rendra compte la
plupart du temps que le problème ne se situe pas là où on pensait qu'il serait. Il faut préserver
son temps pour les choses réellement importantes.
- Se rappeler le principe « Diviser pour mieux régner ». Si
le problème considéré est trop embrouillé, il faut essayer d'imaginer quelles opérations ferait le
programme s'il existait une « entité magique » qui prendrait en charge les parties confuses. Cette
entité est un objet - il suffit d'écrire le code qui utilise l'objet, puis analyser cet objet et
encapsuler ses parties confuses dans d'autres objets, et ainsi de suite.
- Séparer le créateur de la classe de l'utilisateur de la classe (le
programmeur client).L'utilisateur de la classe est le
« client », il n'a pas besoin et ne veut pas savoir ce qui se passe dans les coulisses de la
classe. Le créateur de la classe doit être un expert en conception et écrire la classe de manière à
ce qu'elle puisse être utilisée même par le plus novice des programmeurs, tout en remplissant
efficacement son rôle dans le fonctionnement de l'application. L'utilisation d'une bibliothèque ne
sera aisée que si elle est transparente.
- Quand on crée une classe, essayer de choisir des noms explicites
afin de rendre les commentaires inutiles. Le but est de présenter une interface
conceptuellement simple au programmeur client. Ne pas hésiter à surcharger une méthode si cela est
nécessaire pour proposer une interface intuitive et facile à utiliser.
- L'analyse et la conception doivent fournir, au minimum, les
classes du système, leur interface publique et leurs relations avec les autres classes, en
particulier les classes de base. Si la méthodologie de conception produit plus de
renseignements que cela, il faut se demander si tout ce qui est produit a de la valeur pendant
toute la durée du vie du programme. Si ce n'est pas le cas, les maintenir sera coûteux. Les membres
d'une équipe de développement tendent à « oublier » de maintenir tout ce qui ne contribue pas
à leur productivité ; ceci est un fait que beaucoup de méthodologie de conception
négligent.
- Tout automatiser. D'abord écrire le code de test (avant
d'écrire la classe), et le garder avec la classe. Automatiser l'exécution des tests avec un
Makefile ou un outil similaire. De cette manière, chaque changement peut être automatiquement
vérifié en exécutant les tests, et les erreurs seront immédiatement détectées. La présence du filet
de sécurité que constitue la suite de tests vous donnera plus d'audace pour effectuer des
changements radicaux quand vous en découvrirez le besoin. Se rappeler que l'amélioration la plus
importante dans les langages de programmation vient des tests intégrés fournis par la vérification
de type, la gestion des exceptions, etc. mais que ces tests ne sont pas exhaustifs. Il est de votre
responsabilité de fournir un système robuste en créant des tests qui vérifient les fonctionnalités
spécifiques à votre classe ou votre programme.
- D'abord écrire le code de test (avant d'écrire la classe) afin de
vérifier que la conception de la classe est complète. Si on ne peut écrire le code de
test, c'est qu'on ne sait pas ce à quoi la classe ressemble. De plus, l'écriture du code de test
montrera souvent de nouvelles fonctionnalités ou contraintes requises dans la classe - ces
fonctionnalités ou contraintes n'apparaissent pas toujours dans la phase d'analyse et de
conception. Les tests fournissent aussi des exemples de code qui montrent comment utiliser la
classe.
- Tous les problèmes de conception logicielle peuvent être
simplifiés en introduisant un niveau supplémentaire dans l'abstraction. Cette règle
fondamentale de l'ingénierie logicielle [85]
constitue la base de l'abstraction, la fonctionnalité première de la programmation orientée
objet.
- Toute abstraction doit avoir une signification (à mettre
en parallèle avec la règle no 9). Cette signification peut être aussi
simple que « mettre du code fréquemment utilisé dans une méthode ». Si on ajoute des abstractions
(abstraction, encapsulation, etc.) qui n'ont pas de sens, cela est aussi futile que de ne pas en
rajouter.
- Rendre les classes aussi atomiques que possible. Donner à
chaque classe un but simple et précis. Si les classes ou la conception du système gagnent en
complexité, diviser les classes complexes en classes plus simples. L'indicateur le plus évident est
bien sûr la taille : si une classe est grosse, il y a des chances qu'elle en fasse trop et devrait
être découpée.
|
|
|
|
Clues to suggest redesign of a class
are:
|
|
Les indices suggérant la reconception d'une classe sont :
|
|
|
|
1) A complicated switch statement: consider
using polymorphism.
2) A large number of methods
that cover broadly different types of operations: consider using several
classes.
3) A large number of member variables
that concern broadly different characteristics: consider using several
classes.
|
|
- Une instruction switch compliquée : utiliser le polymorphisme
;
- Un grand nombre de méthodes couvrant un large éventail d'opérations
différentes : utiliser plusieurs classes ;
- Un grand nombre de variables membres couvrant des caractéristiques
fondamentalement différentes : utiliser plusieurs classes.
|
|
|
|
Watch
for long argument lists. Method calls then become difficult to write, read,
and maintain. Instead, try to move the method to a class where it is (more)
appropriate, and/or pass objects in as
arguments. Don’t
repeat yourself. If a piece of code is recurring in many methods in derived
classes, put that code into a single method in the base class and call it from
the derived-class methods. Not only do you save code space, you provide for easy
propagation of changes. Sometimes the discovery of this common code will add
valuable functionality to your
interface. Watch
for switch statements or chained if-else clauses. This is
typically an indicator of type-check coding, which means you are choosing
what code to execute based on some kind of type information (the exact type may
not be obvious at first). You can usually replace this kind of code with
inheritance and polymorphism; a polymorphic method call will perform the type
checking for you, and allow for more reliable and easier
extensibility. From
a design standpoint, look for and separate things that change from things that
stay the same. That is, search for the elements in a system that you might
want to change without forcing a redesign, then encapsulate those elements in
classes. You can learn significantly more about this concept in Thinking in
Patterns with Java, downloadable at
www.BruceEckel.com. Don’t
extend fundamental functionality by subclassing. If an interface element is
essential to a class it should be in the base class, not added during
derivation. If you’re adding methods by inheriting, perhaps you should
rethink the
design. Less
is more. Start with a minimal interface to a class, as small and simple as
you need to solve the problem at hand, but don’t try to anticipate all the
ways that your class might be used. As the class is used, you’ll
discover ways you must expand the interface. However, once a class is in use you
cannot shrink the interface without disturbing client code. If you need to add
more methods, that’s fine; it won’t disturb code, other than forcing
recompiles. But even if new methods replace the functionality of old ones, leave
the existing interface alone (you can combine the functionality in the
underlying implementation if you want). If you need to expand the interface of
an existing method by adding more arguments, create an overloaded method with
the new arguments; this way you won’t disturb any existing calls to the
existing
method. Read
your classes aloud to make sure they’re logical. Refer to the
relationship between a base class and derived class as “is-a” and
member objects as
“has-a.” When
deciding between inheritance and composition, ask if you need to upcast to the
base type. If not, prefer composition (member objects) to inheritance. This
can eliminate the perceived need for multiple base types. If you inherit, users
will think they are supposed to
upcast. Use
data members for variation in value and method overriding for variation in
behavior. That is, if you find a class that uses state variables along with
methods that switch behavior based on those variables, you should probably
redesign it to express the differences in behavior within subclasses and
overridden
methods. Watch
for overloading. A method should not conditionally execute code based on the
value of an argument. In this case, you should create two or more overloaded
methods
instead. Use
exception hierarchies—preferably derived from specific appropriate
classes in the standard Java exception hierarchy. The person catching the
exceptions can then catch the specific types of exceptions, followed by the base
type. If you add new derived exceptions, existing client code will still catch
the exception through the base
type. Sometimes
simple aggregation does the job. A “passenger comfort system” on
an airline consists of disconnected elements: seat, air conditioning, video,
etc., and yet you need to create many of these in a plane. Do you make private
members and build a whole new interface? No—in this case, the components
are also part of the public interface, so you should create public member
objects. Those objects have their own private implementations, which are still
safe. Be aware that simple aggregation is not a solution to be used often, but
it does
happen. Consider
the perspective of the client programmer and the person maintaining the
code. Design your class to be as obvious as possible to use. Anticipate the
kind of changes that will be made, and design your class so that those changes
will be
easy. Watch
out for “giant object syndrome.” This is often an affliction of
procedural programmers who are new to OOP and who end up writing a procedural
program and sticking it inside one or two giant objects. With the exception of
application frameworks, objects represent concepts in your application, not the
application. If
you must do something ugly, at least localize the ugliness inside a
class. If
you must do something nonportable, make an abstraction for that service and
localize it within a class. This extra level of indirection prevents the
nonportability from being distributed throughout your program. (This idiom is
embodied in the Bridge
Pattern). Objects
should not simply hold some data. They should also have well-defined
behaviors. (Occasionally, “data objects” are appropriate, but only
when used expressly to package and transport a group of items when a generalized
container is
innappropriate.) Choose
composition first when creating new classes from existing classes. You
should only used inheritance if it is required by your design. If you use
inheritance where composition will work, your designs will become needlessly
complicated. Use
inheritance and method overriding to express differences in behavior, and fields
to express variations in state. An extreme example of what not to do is
inheriting different classes to represent colors instead of using a
“color”
field. Watch
out for variance. Two semantically different objects may have
identical actions, or responsibilities, and there is a natural temptation to try
to make one a subclass of the other just to benefit from inheritance. This is
called variance, but there’s no real justification to force a
superclass/subclass relationship where it doesn’t exist. A better solution
is to create a general base class that produces an interface for both as derived
classes—it requires a bit more space, but you still benefit from
inheritance, and will probably make an important discovery about the
design. Watch
out for limitation during inheritance. The clearest designs add new
capabilities to inherited ones. A suspicious design removes old capabilities
during inheritance without adding new ones. But rules are made to be broken, and
if you are working from an old class library, it may be more efficient to
restrict an existing class in its subclass than it would be to restructure the
hierarchy so your new class fits in where it should, above the old
class. Use
design patterns to eliminate “naked functionality.” That is, if
only one object of your class should be created, don’t bolt ahead to the
application and write a comment “Make only one of these.” Wrap it in
a singleton. If you have a lot of messy code in your main program that creates
your objects, look for a creational pattern like a factory method in which you
can encapsulate that creation. Eliminating “naked functionality”
will not only make your code much easier to understand and maintain, it will
also make it more bulletproof against the well-intentioned maintainers that come
after
you. Watch
out for “analysis paralysis.” Remember that you must usually
move forward in a project before you know everything, and that often the best
and fastest way to learn about some of your unknown factors is to go to the next
step rather than trying to figure it out in your head. You can’t know the
solution until you have the solution. Java has built-in firewalls; let
them work for you. Your mistakes in a class or set of classes won’t
destroy the integrity of the whole
system. When
you think you’ve got a good analysis, design, or implementation, do a
walkthrough. Bring someone in from outside your group—this
doesn’t have to be a consultant, but can be someone from another group
within your company. Reviewing your work with a fresh pair of eyes can reveal
problems at a stage when it’s much easier to fix them, and more than pays
for the time and money “lost” to the walkthrough
process.
|
|
Eviter les longues listes d'arguments. Les appels de
méthode deviennent alors difficiles à écrire, lire et maintenir. A la place, essayer de déplacer la
méthode dans une classe plus appropriée, et / ou passer des objets en tant
qu'arguments.
Ne pas se répéter. Si une portion de code est récurrente
dans plusieurs méthodes dans des classes dérivées, mettre ce code dans une méthode dans la classe
de base et l'appeler depuis les méthodes des classes dérivées. Non seulement on gagne en taille de
code, mais de plus les changements seront immédiatement effectifs. De plus, la découverte de ces
parties de code commun peut ajouter des fonctionnalités intéressantes à l'interface de la
classe.
Eviter les instructions switch ou les if-else
enchaînés. Ceci est typiquement un indicateur de code
vérificateur de type, ce qui implique qu'on choisit le code à exécuter suivant une
information concernant le type (le type exact peut ne pas être évident à première vue). On peut
généralement remplacer ce genre de code avec l'héritage et le polymorphisme ; un appel à une
méthode polymorphique se charge de la vérification de type pour vous, et permet une extensibilité
plus sûre et plus facile.
Du point de vue de la conception, rechercher et séparer les choses
qui changent des choses qui restent les mêmes. C'est à dire, trouver les éléments du
système qu'on pourrait vouloir changer sans forcer une reconception, puis encapsuler ces éléments
dans des classes. Ce concept est largement développé dans Thinking in Patterns with Java,
téléchargeable à www.BruceEckel.com.
Ne pas étendre les fonctionnalités fondamentales dans les
sous-classes. Si un élément de l'interface est essentiel dans une classe il doit se
trouver dans la classe de base, et non pas rajouté par dérivation. Si on ajoute des méthodes par
héritage, la conception est peut-être à revoir.
Moins c'est mieux. Partir d'une interface minimale pour
une classe, aussi restreinte et simple que possible pour résoudre le problème, mais ne pas essayer
d'anticiper toutes les façons dont la classe pourrait être utilisée. Au fur et à mesure
que la classe sera utilisée, on découvrira de nouvelles fonctionnalités à inclure dans l'interface.
Cependant, une fois qu'une classe est utilisée, son interface ne peut être réduite sans perturber
le code des classes clientes. S'il y a besoin d'ajouter de nouvelles méthodes, cela ne pose pas de
problèmes : on ne force qu'une recompilation du code. Mais même si de nouvelles méthodes remplacent
les fonctionnalités d'anciennes méthodes, il ne faut pas toucher à l'interface (on peut toujours
combiner les fonctionnalités dans l'implémentation sous-jacente si on veut). Si on veut étendre
l'interface d'une méthode existante en ajoutant des arguments, créer une méthode surchargée avec
les nouveaux arguments : de cette manière les appels à la méthode existante n'en seront pas
affectés.
Relire la hiérarchie de classes à voix haute pour s'assurer de sa
logique. Les relations se lisent « est-une » entre classe de base et classe dérivées, et «
a-un » entre classe et objet membre.
Se demander si on a besoin de surtyper jusqu'au type de base avant
de choisir entre héritage et composition. Préférer la composition (objets membres) à
l'héritage si on n'a pas besoin du transtypage ascendant. Cela permet d'éliminer le besoin d'avoir
de nombreux types de base. Si l'héritage est utilisé, les utilisateurs croiront qu'ils sont
supposés surtyper.
Utiliser des données membres pour le stockage des valeurs, et des
redéfinitions de fonctions pour des modifications de comportement. Autrement dit, une
classe qui utilise des variables d'état en conjonction avec des méthodes qui modifient leur
comportement suivant la valeur de ces variables devrait être repensée afin d'exprimer les
différences de comportement au sein de classes dérivées et de méthodes redéfinies.
Utiliser la surcharge. Une méthode ne doit pas se baser
sur la valeur d'un argument pour choisir quelle portion de code exécuter. Si le cas se présente, il
faut créer deux (voire plus) méthodes surchargées.
Utiliser les hiérarchies d'exceptions - préférablement
dérivées des classes spécifiques appropriées de la hiérarchie standard d'exceptions de Java. La
personne capturant les exceptions peut alors capturer les types spécifiques d'exceptions, suivies
du type de base. Si de nouvelles exceptions dérivées sont ajoutées, le code client continuera de
capturer les exceptions à travers le type de base.
Un simple agrégat peut suffire pour accomplir le travail.
Dans un avion, le confort des passagers est assuré par des éléments totalement indépendants :
siège, air conditionné, vidéo, etc. et pourtant ils sont nombreux dans un avion. Faut-il créer des
membres privés et construire une nouvelle interface ? Non - dans ce cas, les éléments font partie
de l'interface publique ; il faut donc créer des objets membres publics. Ces
objets ont leur propre implémentation privée, qui reste sûre. Un agrégat n'est pas une solution
fréquemment rencontrée, mais cela arrive.
Se placer du point de vue du programmeur client et de la personne
maintenant le code. Concevoir les classes afin qu'elles soient aussi évidentes que
possibles à utiliser. Anticiper le type de changements qui seront effectués, et concevoir les
classes afin de faciliter l'introduction de ces changements.
Surveiller qu'on n'est pas victime du « syndrome de l'objet géant
». Ce syndrome afflige souvent les programmeurs procéduraux nouvellement convertis à la
POO qui se retrouvent à écrire des programmes procéduraux en les encapsulant dans un ou deux
énormes objets. A l'exception des environnements de développement, les objets représentent des
concepts dans l'application et non l'application elle-même.
Si on doit incorporer une horreur au système, au moins localiser
cette horreur à l'intérieur d'une classe.
Si on doit incorporer une partie non portable, créer une
abstraction pour ce service et le localiser à l'intérieur d'une classe. Ce niveau
supplémentaire d'abstraction empêche la non-portabilité de s'étendre au reste du programme (cet
idiome est repris par l'image du Pont).
Les objets ne doivent pas seulement contenir des données.
Ils doivent aussi avoir des comportements bien définis (occasionnellement, des « objets de données
» peuvent être appropriés, mais seulement s'ils sont utilisés pour empaqueter et transporter un
groupe d'articles là où un conteneur plus général ne répondrait pas aux besoins).
Privilégier la composition lorsqu'on crée de nouvelles classes à
partir de classes existantes. L'héritage ne doit être utilisé que s'il est requis par la
conception. Si l'héritage est utilisé là où la composition aurait suffi, la modélisation deviendra
inutilement compliquée.
Utiliser l'héritage et la redéfinition de méthodes pour exprimer
les différences de comportement, et des variables pour exprimer des variations dans
l'état. Un exemple extrême de ce qu'il ne faut pas faire est de dériver différentes
classes pour représenter des couleurs plutôt qu'utiliser un champ « couleur ».
Se méfier de la variance. Deux objets sémantiquement différents peuvent avoir des actions ou des responsabilités
identiques, et il est tentant de faire de l'une une sous-classe de l'autre pour bénéficier de
l'héritage. Ceci est appelé la variance, mais il n'y a aucune raison d'instaurer une
relation superclasse / classe dérivée là où il n'y en a pas. Une meilleure solution est de créer
une classe de base générique qui produise une interface pour les deux classes dérivées - cela
nécessite un peu plus de place, mais on bénéficie toujours de l'héritage, et on va probablement
faire une découverte importante concernant la modélisation.
Eviter les limitations introduites durant un
héritage. Les conceptions les plus claires ajoutent de
nouvelles capacités à des classes dérivées. Les conceptions suspectes enlèvent des capacités durant
l'héritage sans en ajouter de nouvelles. Mais les règles sont faites pour être transgressées, et si
on travaille avec une ancienne bibliothèque de classes, il peut être plus efficace de restreindre
une classe existante dans la classe dérivée que de restructurer la hiérarchie afin que la nouvelle
classe s'intègre là où elle devrait, c'est à dire au dessus de la vieille classe.
Utiliser les patrons de conception (design patterns) pour éliminer
les « fonctionnalités non cachées ». C'est à dire que si un seul objet de la classe doit
être créé, ne pas livrer la classe telle quelle à l'application avec un commentaire du genre : « Ne
créer qu'un seul de ces objets. » ; il faut l'encapsuler dans un singleton. Si le programme
principal comporte trop de code embrouillé destiné à créer les objets de l'application, rechercher
un modèle de création, par exemple une méthode propriétaire dans laquelle on pourrait encapsuler la
création de ces objets. Eliminer les « fonctionnalités non cachées » rendra le code non seulement
plus facile à comprendre et maintenir, mais il le blindera aussi contre les mainteneurs bien
intentionnés qui viendront après.
Eviter la « paralysie analytique ». Se rappeler qu'il
faut avancer dans un projet avant de tout savoir, et que parfois le meilleur moyen d'en apprendre
sur certains facteurs inconnus est de passer à l'étape suivante plutôt que d'essayer de les
imaginer. On ne peut connaître la solution tant qu'on ne l'a pas. Java possède des pare-feux
intégrés ; laissez-les travailler pour vous. Les erreurs introduites dans une classe ou un ensemble
de classes ne peuvent détruire l'intégrité du système dans son ensemble.
Faire une relecture lorsqu'on pense avoir une bonne analyse,
conception ou implémentation. Demander à une personne extérieure au groupe - pas
obligatoirement un consultant, cela peut très bien être quelqu'un d'un autre groupe de
l'entreprise. Faire examiner le travail accompli par un oeil neuf peut révéler des problèmes durant
une phase où il est plus facile de les corriger, et largement compenser le temps et l'argent «
perdus » pendant le processus de relecture.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|