• Aucun résultat trouvé

Organisation g´en´erale d’un programme Eiffel

Eiffel : une approche globale pour programmer et r´eutiliser

2.3 Organisation g´en´erale d’un programme Eiffel

2.3.1 Structure d’une classe

Une classe (Fig. 2.1)6 est organis´ee en sections, toutes facultatives, sauf celle d’identification, dans l’ordre suivant : indexation, identification, h´eritage, cr´eation, d´eclarations de primitives (attributs (variable d’instance), constantes ou routines) et invariant de classe. La section d’indexation (indexing) contient des informations ignor´ees des compilateurs, mais utilis´ees par des outils d’exploration (browsing)), d’indexation, de gestion de projet... Celle d’identification (class) donne le nom de la classe, ses param`etres ´eventuels de g´en´ericit´e et son statut particulier : abstraite

4. Du latin res,((la chose))et facere,((faire)). En approche objet, r´eifier, c’est d´ecrire explicitement par un objet (donc pr´esent s’il le faut en m´emoire) un concept d’habitude implicite, dispers´e ou inaces-sible durant l’ex´ecution : un type, une classe, une routine, l’extension d’un type, une instruction... Au sens large, toute repr´esentation par objets est une r´eification [Ferber, 1990].

5. Nonprofit International Consortium for EIFFEL.

6. Les premiers exemples d’EIFFELque nous donnons illustrent de mani`ere group´ee plusieurs m´ecanismes expliqu´es dans ce chapitre. Il ne faudra pas chercher `a les comprendre en entier d`es leur apparition, car des explications seront donn´ees plus loin dans le texte.

indexing

description: "Dates de l’`ere chr´etienne,%

% julienne (<= 04/10/1582) ou gr´egorienne (>= 15/10/1582)" key words: date, calendar, christian, julian, gregorian...

revision: "$ Revision: 2.2 $" -- Par exemple, gestion des versions par RCS ...

class DATE

inherit

COMPARABLE -- la clause d’h´eritage compl`ete sera vue plus loin (Fig. 2.7, p. 56) creation make, make0 feature -- Attributs day : INTEGER month : INTEGER year : INTEGER

ORIGIN str : STRING is "01/01/0001" -- origine de l’`ere chr´etienne. feature -- Initialiseurs

make0 is -- Cr´eation initialis´ee `a l’originde l’`ere chr´etienne...

make (d, m, y : INTEGER) is -- Cr´eation d’une date au jourd, moism, ann´eey

feature -- Changement des attributs

update from string (s: STRING) is -- mise `a jour avec la repr´esentations. feature -- Accesseurs secondaires

abscissa : INTEGER is -- Nombre de jours depuisorigin ...

feature -- Op´erations alg´ebriques

infix "-" (other: like Current) : INTEGER is -- Nb de jours dansCurrent-other

feature {NONE} -- Acc`es `a des fonctions externes C

time (t: INTEGER) : INTEGER is -- Acc`es `a time(3) de la biblioth`eque C Standard invariant

date is valid : is valid(Current)

date is christian : Current >= date("01/01/0001") date is credible : Current <= date("31/12/2200")

end -- classe DATE

FIG. 2.1: Structure d’une classe EIFFEL: extrait de la classeDATE.

(deferred2.6.3) ou expans´ee (expanded,§2.4). Par d´efaut, une classe normale n’est ni abstraite, ni expans´ee. La section consacr´ee `a l’h´eritage (inherit) cite les super-classes avec des clauses ´eventuelles d’adaptations. La liste des initialiseurs (creation) donne le nom des proc´edures `a utiliser pour initialiser les instanciations. Les sections de d´eclarations des primitives (feature) sont regroup´ees par cat´egories fonctionnelles (`a la mani`ere des protocoles de SMALLTALK) et pr´ecisent leur ex-portation (§2.5). L’invariant de la classe (invariant) est l’une des trois cat´egories d’assertions externes (§2.8.3).

Comme nous l’avons vu dans lechapitre 1, le concept de classe peut s’appr´ehen-der de deux points de vue, intensionnel ou extensionnel. Pour le g´enie logiciel, ces points de vues correspondent respectivement aux aspects modulaires et s´emantiques.

Du point de vue modulaire, les classes jouent le rˆole d’unit´es de structuration

(seule unit´e pour d´ecrire un composant), de programmation (sous la responsabilit´e d’un seul programmeur) et de stockage (une et une seule classe par fichier). Les primitives d’une classe se composent de constantes (simples ou structur´ees), d’attri-buts et de routines (m´ethodes), proc´edures ou fonctions. Une routine peut d´eclarer des variables locales de n’importe quel type, mais ne peut contenir d’autres routines

ou d’autres classes. Le langage ne dispose pas de la notion de bloc, pour inciter le programmeur `a localiser tous les traitements dans des routines r´eutilisables. La taille des corps de routines est donc le plus souvent r´eduite (en moyenne, cinq ou six lignes), mais le surco ˆut `a l’ex´ecution de cette atomisation est le plus souvent ´elimin´e par les optimiseurs (§2.9).

Du point de vue s´emantique, les classes impl´ementent des types abstraits.

Cha-que classe d´ecrit un type (par exemple la classe DATE), ou plusieurs si elle est g´en´erique (par exempleLIST[DATE], LIST[WINDOW]...). Inversement, tous les types, mˆeme ceux qui sont de base (INTEGER, BOLLEAN, STRING, ARRAY...) sont d´ecrits en EIFFELdans des classes de biblioth`eque, donc adaptables par h´eritage. Les impl´e-mentations traitent cependant les types de base de mani`ere particuli`ere pour l’op-timisation, tant qu’ils ne sont pas red´efinis. Les proc´edures jouent le rˆole de

modi-fieurs ou d’initialiseurs de l’´etat des instances et les fonctions celui d’accesseurs,

pour consulter leur ´etat, sans effet de bord apparent.

2.3.2 Classe par opposition `a syst`eme

La mod´elisation des proc´ed´es de d´eveloppement logiciel (software processes) qui sous-tendent les diff´erentes activit´es d’un projet logiciel (conception, sp´ecifica-tion, impl´ementasp´ecifica-tion, r´eutilisation...) n´ecessite d’autres concepts que les construc-tions syntaxiques d’un programme, en particulier dans un cadre r´eparti ou persistant. L’approche EIFFELfait le minimum, mais distingue quelques entit´es comme les no-tions d’univers, de classe, de syst`eme, de r´epertoire de classe, etc., grˆace `a l’emploi de deux langages bien distincts, EIFFELet LACE, ayant chacun sa propre syntaxe, quoique de style identique. EIFFELest utilis´e pour d´ecrire les classes, ind´ependam-ment de tout usage, alors que LACEsert `a pr´eciser l’usage des classes pour cons-truire un programme ex´ecutable, appel´e syst`eme dans la terminologie EIFFEL. Cette s´eparation est fondamentale pour ne pas polluer le texte des classes d’informations li´ees aux conditions d’ex´ecutions, donc instables et non r´eutilisables, puisque clas-ses et syst`emes ont des cycles de vie tr`es diff´erents. Cela ´evite l’erreur des langages qui placent, dans le texte mˆeme des classes, des indications destin´ees au compilateur pour l’aider pour les optimisations :pragma(ADA),register(C),virtual(C++),

native(JAVA)...

Un texte en LACE(appel´e Ace, Assemblage de Composants EIFFEL) d´ecrit donc un((m´etasyst`eme))qui contrˆole la construction et l’´evolution d’un syst`eme. Un Ace indique la classe racine — dont une routine d’initialisation joue le rˆole de routine principale — l’univers du syst`eme, l’armement des assertions, les options d’opti-misation et de traduction dans des paquetages portables en C ANSI ou en

byte-code JAVA, ´equip´es d’un Makefile. L’univers d’un syst`eme pr´ecise l’organisation hi´erarchique des r´epertoires de classes (clusters) qui participent `a sa construction, avec d’´eventuels renommages en cas de conflits. Les fichiers Ace jouent aussi le rˆole de fichiers de configuration et dispensent de l’usage de Makefiles qui, de toute fac¸on, seraient inaptes `a d´ecrire les d´ependances circulaires qui peuvent exister entre les classes EIFFEL.

feature -- Accesseurs secondaires origin : DATE is

-- Origine de l’`ere chr´etienne d´efinie dansOrigin str. once Result := date("01/01/0001")

ensure

christian(Result.year) ; Result.out.is equal(Origin str)

end

year day : INTEGER is

-- Num´ero du jour dans l’ann´ee, en commen¸cant `a 1 pour le 1er janvier. local m : INTEGER

do

if (year = 1582) and (month = 10) then if day <= 4 then

Result := day

else check day >= 15 end

Result := day - 10

end else

Result := day

end

from m := 1 until m = month loop

Result := Result + days in month(m, year) ; m := m+1

end ensure

greater than min: Result >= 1

less than year max: not is leap year(year) implies Result <= 365 less than leap year max: is leap year(year) implies Result <= 366

end -- year day

feature -- Op´erations alg´ebriques

infix "-" (other: like Current) : INTEGER is -- Nombre de jours dansCurrent-other

do Result := abscissa - other.abscissa

ensure Result = abscissa - other.abscissa

end -- infix ”-”

infix "-y", difference in years (other: like Current) : REAL is -- Nombre d’ann´ees gr´egoriennes dansCurrent-other

do Result := (Current - other) / 365.2425

ensure Result = (Current - other) / 365.2425

end --infix”-y”, difference in years

FIG. 2.2: Exemples de routines de la classeDATE.

2.3.3 Aspects syntaxiques

La syntaxe EIFFELressemble `a celle du langage ADA, avec une soixantaine de mots r´eserv´es. Elle autorise les r´ef´erences en avant et les ‘;’ sont facultatifs en fin de ligne. Les commentaires d´ebutent par des “--” jusqu’`a la fin de ligne ; certains commentaires ont une position cit´ee dans la syntaxe et jouent un rˆole pr´ecis qui est exploit´e par des outils de documentation automatique : cat´egorie fonctionnelle d’une sectionfeature, r´esum´e de sp´ecification d’une primitive en langage naturel. Des marqueurs de commentaires peuvent pr´eciser leur intention, ou les supprimer de la vue des clients s’ils commencent par “--|”.

La structure d’une routine se compose (cf.year day, Fig. 2.2 oucreer, Fig. 2.3, p. 46) d’un nom, d’une signature, d’une sp´ecification r´esum´ee en commentaires, d’une pr´econdition (require), d’une section de d´eclarations de variables locales ( lo-cal), d’un corps de routine, d’une postcondition (ensure) et enfin d’une clause de rescousse (rescue) pour traiter les exceptions. La signature indique les param`etres de la routine et, s’il s’agit d’une fonction, le type de l’objet retourn´e. Le corps de rou-tine peut ˆetre concret avec des instructions d’impl´ementation (doouonce), retard´ees (deferred) ou d´efini dans un autre langage (external), typiquement en C. Les routines

oncene sont ex´ecut´ees qu’une seule fois, grˆace `a un((drapeau secret))produit par le compilateur. Les routines retard´ees sont d´efinies plus tard, dans des sous-classes. De toutes les sections d’une routine, seuls le nom, la signature et le corps sont obli-gatoires. Le langage ne dispose que d’un jeu minimal d’instructions pour d´ecrire les comportements : instanciation (!!), appel de routine, affectation directe (:=) ou une tentative d’affectation (?=), choix logique (if...then...elseif...then...else...end), choix discret (inspect...when...then...when...then...else...end), it´eration (from

...invariant...variant...until...loop...end). Le transfert de contrˆole se fait toujours `a la fin de chaque construction et EIFFELne dispose d’aucune construction comme

return, break, continue, exit...

Pour atteindre les composantes d’un objet, EIFFEL utilise une seule notation

qualifi´ee (acc`es navigationnel) avec l’op´erateur de travers´ee‘.’. Comme en pro-grammation fonctionnelle ou par l’utilisation de tubes dans le syst`eme UNIX, une expression peut se composer avec d’autres expressions, sans limitation, car toute

ex-pression EIFFELrenvoie un objet. Celui-ci peut servir pour une affectation, comme

argument d’une routine ou d’un op´erateur ; mais on peut lui appliquer aussi n’im-porte quelle primitive export´ee, de consultation ou de modification. Ce processus de composition peut se poursuivre, tant que les primitives travers´ees sont des consulta-tions (variable, constante ou appel de fonction). Ainsi, avec les notaconsulta-tions :

(x - 1).out.count

dupont.enfants(3).marier(jean)

la premi`ere expression calcule le nombre de caract`eres n´ecessaires `a la repr´esentation sous forme d’une chaˆıne de caract`eres, du nombre qui repr´esente la valeurx−1; la seconde notation est une instruction qui marie l’objetjeanau troisi`eme enfant de l’objetdupont(normalement une fille, ce qui sera contrˆol´e par les assertions).

En EIFFEL, les variables7qui peuvent d´esigner un objet sont les attributs (va-riable d’instance), les param`etres et les va(va-riables locales de routines. Implicitement, toute qualification d’une expression commence par le contexte courant. Celui-ci est compos´e des variables de la routine et de celles de sa classe, mais sans le m´ecanisme habituel de masquage des autres langages `a structure de bloc. Ainsi,xetdupontsont des variables directement visibles dans le contexte des expressions pr´ec´edentes. Les mots r´eserv´esCurrentetResult(cf. year day, Fig. 2.2, p. 42) d´esignent des va-riables pr´ed´efinies qui font r´ef´erence respectivement `a l’instance elle-mˆeme et `a l’objet retourn´e `a la sortie d’une fonction.

La d´efinition d’une constante d’un type de base (INTEGER, CHARACTER, REAL, DOUBLE, BOOLEAN, STRING, ARRAY) peut utiliser une constante litt´erale, comme pour la chaˆıneORIGIN str(cf. Fig. 2.1, p. 40). Les tableaux litt´eraux sont indiqu´es

par une liste d’expressions entre guillemets ((( expr1, expr2... exprn ))). Ils ser-vent aussi `a la transmission de listes de param`etres effectifs pour les routines qui admettent un nombre variable d’arguments. Une routine peut ainsi avoir une ou plu-sieurs listes d’arguments dont le type peut ˆetre quelconque commeARRAY[ANY]ou beaucoup plus restreint commeARRAY[OPTION]ouARRAY[INTEGER]. Ainsi l’exemple suivant :

-- d´efinition d’une fonction avec deux listes `a nombre variable d’arguments :

list dir (options : ARRAY [ OPTION ] ; files : ARRAY [ STRING ]) is...

-- utilisation de cette fonction :

list dir ((( long, recursive, block )), (( "fichier1", "fichier2" )) )

montre l’utilisation d’une commande de listage de fichiers avec une liste d’options et une liste de noms de fichiers. Ce m´ecanisme que nous avons sugg´er´e `a Bertrand Meyer, combine la souplesse des langages dynamiques pour le nombre des argu-ments avec un contrˆole statique des types des arguargu-ments de chaque liste.

Pour d´efinir une constante d’une classe quelconque, ou lorsque la d´efinition doit ˆetre retard´ee jusqu’`a la phase d’initialisation du programme, on utilise le m´ecanisme d’´evaluation unique des routinesonce(cf.Origin, Fig. 2.2, p. 42). EIFFELcompense en partie l’absence de variables de classe par le m´ecanisme des fonctionsoncepour construire des objets instanci´es une seule fois, donc uniques et partag´es entre toutes les instances de la classe. Cela ne permet pas de changer d’objet partag´e, mais on peut le modifier. Dans le cas d’un syst`eme persistant, la s´emantique de((une seule fois))manque cependant de pr´ecision.

EIFFELautorise la red´efinition de la plupart des op´erateurs pr´ed´efinis, pr´efixes (-,not...) ou infixes (<=,*,and...), mais pas celle des op´erateurs d’´egalit´e (=,6=) et d’affectation (:=,?=) dont la s´emantique g´en´erale est stable. L’absence de surcharge des op´erateurs8 est compens´ee par la possibilit´e d’en d´efinir un nombre illimit´e, pr´efixes ou infixes, avec un symbole form´e de n’importe quelle suite de caract`eres qui commence par+,-,*,/,<,>,=,\,^,|,@,#ou&[Meyer, 1997]. Ainsi, dans la classeDATE(cf. Fig. 2.2, p. 42), l’op´erateur ‘-’ exprime une diff´erence en jours et l’op´erateur ‘-y’ une diff´erence en ann´ees.