).   Et
pourtant,  en  interne,   +  a  déja   plusieurs
significations, comme dans 2+4 et 'a'+'nimal'.
1.4 La gestion modulaire
Cette    gestion    par    fichier    inclus,   modules
pré-compilés  (unités)  est lourde
et  inadaptée  à  un développement
modulaire :   charger  toute une  unité pour une
seule procédure  (crt pour  clrscr par  exemple)
est lourd.   Quand  on extrait  un module  d'un fichier
inclus et qu'on le  modifie pour un nouveau  programme,
on se  retrouve avec  deux versions  ressemblantes d'un
même  module.    Le  réinjecter  dans   le
fichier originel  après modification  obligerait
à   revérifier   tous   les    programmes
utilisant ce fichier...
1.5 Les expressions régulières
On peut écrire if c in ['o','n'] mais pas if c ~
[A-Z][A-Z0-9]*   qui   signifierait   si  c  correspond
à une  lettre suivie  d'un nombre  quelconque de
lettres ou chiffres.
1.6   Les   tableaux   associatifs   (ou   l'indexation
généralisée)
Par exemple T["oui"] ne peut être utilisé.
Pourtant,    ce     genre    d'indexation     simplifie
énormément les recherches, les comptages.
2. Vers le Turbo Pascal Objet
2.1 Exemple de la surcharge
Nous nous limiterons dans un premier temps à  la
notion    de    surcharge    des    noms   de   modules
(procédures  ou   fonctions).     Prenons  comme
exemple celui de la lecture et de l'écriture  de
trois nombres :  nbe (un entier), nbr (un  réel)
et  nbf  (une  fraction).    Un  exemple  plus  concret
pourrait  être   celui  de   la  lecture   et  de
l'écriture de trois nombres  :  k (un  nombre de
kilos), p  (un prix),  n (un  numéro de facture)
mais pour  des raisons  exposées plus  loin nous
préférons nbe,  nbr et  nbf.   Prenons un
algorithme simple de demande de nbe et nbr :
{ Demande de nbe et nbr }
écrire " donner un entier "
lire nbe
écrire " et aussi un réeel "
lire nbr.
Le typage  des variables  nbe et  nbr est (humainement)
explicite d'après le contexte et les messages de
demande.  Si ce  n'avait pas été le  cas,
fidèles à nos principes, des  indications
de   typage   auraient   été   mises   en
commentaires.    Il   est  facile  d'écrire   un
programme  Pascal  traditionnel  qui  l'exécute.
Par contre, si on rajoute  la demande de nbf, on  tombe
sur  le   problème  de   la  lecture   des  deux
éléments  de  nbf,  à  savoir  son
numérateur  et  son  dénominateur.  Aucun
lecteur n'a de mal à comprendre l'algorithme
{ Demande de nbe, nbr et nbf }
écrire " donner un entier "
lire nbe
écrire " donner aussi un réeel "
lire nbr
écrire " et enfin une fraction "
lire nbf.
La traduction en Pascal classique est  délicate.
Après l'introduction du  type fraction, soit  on
utilise  une   procédure  LitFraction   soit  on
écrit     directement     la     lecture      du
numérateur et du dénominateur.  Les  deux
programmes    correspondants    ne    sont   donc   pas
homogènes  :     soit  readln  n'apparait   plus
explicitement pour lire nbf, soit il apparait deux fois
(une  pour  chaque  élément  de nbf).  On
trouve ci-après  les programmes  associés
avec les deux implémentations possibles du  type
fraction.
program Un ; const num = 1 ; den = 2 ;
type   entier = integer ; réel = real ;
      fraction = array[num..den] of entier ;
(*$I Fraction.Inc *) {qui contient la procédure LitFraction} var nbe :
entier ; nbr : réel ; nbf : fraction ;
begin { Demande de nbe, nbr et nbf }
   writeln('donner un entier ') ;
   readln ( nbe ) ;
   writeln('donner aussi un réeel ') ;
   readln ( nbr ) ;
   writeln('et enfin une fraction ') ;
   LitFraction( nbf )
end.
program Deux ;
type   entier = integer ; réel = real ;
      fraction = record num,den : entier end ;
var nbe : entier ; nbr : réel ; nbf : fraction ;
begin { Demande de nbe, nbr et nbf }
   writeln('donner un entier ') ;
   readln ( nbe ) ;
   writeln('donner aussi un réel ') ;
   readln ( nbr ) ;
   write('et enfin une fraction ') ;
   write('(numérateur puis  et dénominateur puis ) ') ;
   readln( nbf.num );
   readln( nbf.den )
end.
Pour  garder  l'homogénéité  et la
correspondance avec  l'algorithmique, on  peut inventer
un code  de lecture  :   1 signifie  entier, 2 signifie
réel, 3 signifie fraction.  On obtient alors
program Trois ;
(*$I Fraction.Inc *) {qui contient la définition de Fraction}
type entier = integer ; réel = real ; var nbe, nbr, nbf : nombre ;
(*$I Lecture.Inc *)  {qui contient la procédure Lit}
begin { Demande de nbe, nbr et nbf }
   writeln('donner un entier ') ;
   Lit( nbe , 1 ) ;
   writeln('donner aussi un réel ') ;
   Lit( nbr , 2 ) ;
   writeln('et enfin une fraction ') ;
   Lit( nbf , 3 )
end.
Pour  que  la  procédure  Lit  puisse être
déclarée,  il  faut  regrouper  les trois
types   entier,   réel   et   fraction  sous  un
même   type   commun.      On  peut  utiliser  un
enregistrement comme
type nombre = record e : entier ; r : réel ; f : fraction end ;
ou  même,  si  on  ne  veut  pas perdre de place,
utiliser le code de lecture :
type nombre = record case typ : byte of 1 : (e : entier) ; 2 : (r : réel)
; 3 : (f : fraction) end { de case et de record }
Ce type d'écriture  n'est pas satisfaisant.   En
effet, il  faut réécrire  l'algorithme en
explicitant le numéro de code de lecture, ce qui
ne se justifie pas en algorithmique.
2.2 Surcharge et héritage
Regardons maintenant comment on peut résoudre ce
problème  en  termes  d'objets.    Inventons une
classe nommée Nombre  avec trois sous-classes  :
entier, réel et  fraction.  Cela  s'écrit
en Pascal Objet :
type   Nombre   = Object ...       end
      Entier   = Object( Nombre ) end
      Réel     = Object( Nombre ) end
      Fraction = Object( Nombre ) end.
Mais en objet, les  données et les actions  sont
associées.  La définition complète
est donc :
type Nombre   = Object valeur : real ; (* même pour un entier *)
               procedure Lire end
       Entier   = Object( Nombre ) end
     Réel     = Object( Nombre ) end
     Fraction = Object( Nombre )
                num, den : integer ; (* quand même ! *)
                procedure Lire end
procedure Nombre.Lire ; begin
   write(' Entrez un nombre ') ;
   readln(Valeur)
end ;
procedure Fraction.Lire ; begin
   write(' Entrez le numérateur ') ;
   readln(Num) ;
   write(' Entrez le dénominateur ') ;
   readln(Den) ;
   Valeur := Num / Den
end ;
Une remarque s'impose :   la surcharge de Lire pour  un
entier  et  un  réel  est  remplacée  par
l'héritage    (ou     transmission)    de     la
procédure Lire pour un objet.  Par contre, si on
voulait vraiment que les objets entiers aient  vraiment
le   type   entier,   il   faudrait    redéfinir
Entier.Lire.    Le  programme  principal associé
à de telles déclarations est
program Quatre ;
   {... ici les déclarations d'objet précédentes }
   var nbe : entier ; nbr : réel ; nbf : fraction ;
begin { Demande de nbe, nbr et nbf }
   writeln('donner un entier ') ;
   nbe.Lire ;
   writeln('donner aussi un réeel ') ;
   nbr.Lire ;
   writeln('et enfin une fraction ') ;
   nbf.Lire
end.
3. PROGRAMMATION (ORIENTEE) OBJETS
3.1.  Présentation générale
Styles  de  programmation,  modes  de  programmation ou
simplement mode des années 90, la  Programmation
Objet (PO)  et la  Programmation Orientée  Objet
(POO)    sont    très    présentes   dans
l'informatique, que ce soit sous forme de logiciels, de
livres  ou  simplement  de  sujet  à discussion.
Nous  essaierons  ici  de  présenter  la  notion
d'objet  et  d'éclairer  les  différences
entre  PO   et  POO.     Pour   cela,  au   travers  du
problème  classique  de  Comar  on se servira de
divers      langages      représentatifs     des
différentes tendances.
3.2.  Notion d'Objets
En  programmation  classique,  on  compare  souvent les
algorithmes à des recettes de cuisine pour  bien
montrer l'aspect "déroulemental", la  "technique
manipulatoire"  du  travail  à  effectuer.  Pour
continuer  l'analogie,  de  même  qu'une  recette
consiste en  une liste  d'ingrédients et  en une
méthode  de  préparation,  un  objet  est
composé   de    données   et    d'actions
liées  à  ces  objets.   Par exemple, une
disquette  peut  être  identifiée  par ses
caractéristiques (taille,secteurs  etc.) et  les
fonctions qu'elle supporte (lire, écrire  etc.).
Cette  description   est  plus   un  modèle   de
disquette qu'une disquette réelle et en ce sens,
on parlera plutôt  de la "classe"  des disquettes
plutôt   que   de   l'objet    "théorique"
nommé disquette.
Quatre   notions   sont    fondamentales   en   PO    :
l'encapsulation, l'héritage et le  polymorphisme
et   l'envoi   de   messages.      L'encapsulation  est
l'intégration des différentes parties  de
l'objet  (données  et  programmes,  structure et
opérations,    champs    et     méthodes,
caractéristiques et  opérations) dans  un
même ensemble tout  en masquant le  détail
de   l'écriture,   comme   dans   le   cas    de
bibliothèques           de            programmes
précompilées.  Un objet devient alors une
"boite  noire"  avec  qui  on  ne  communique  que  par
message.    L'héritage  permet transférer
des  propriétés  ou  des  actions   d'une
classe         à         une         sous-classe
spécialisée.     Ainsi,  la   sous-classe
Ecrans        issue         de        la         classe
Périphériques_de_Sorties hérite de
l'action   Initialisation.       Cet    héritage
définit une structuration  à la fois  des
données   en    elles-   mêmes    et   des
données  entre  elles,  ce  que  ne  fait pas la
programmation   classique.       Au    delà   du
regroupement  et  de   la  cohérence  des   sous
programmes,   cela    donne   une    architecture   des
donnés  comparable  à  l'architecture des
programmes.   Cela permet  en particulier  de retrouver
rapidement tout ce qui  concerne un même type  de
donnée (les entiers,  les fichiers, les  graphes
etc.).   Le polymorphisme  est le  regroupement sous le
même     nom     d'actions     différentes
liées aux objets.   Ainsi les sous-objets de  la
classe Polygones (tels triangles, rectangles etc.)  ont
la  même  méthode  Périmètre
dont la définition est différente suivant
l'objet.  Associé à l'héritage, le
polymorphisme                assure                 une
homogénéité quant aux actions,  un
masquage  de  la  définition.   Le polymorphisme
dont   un   exemple   simple   est   la  surcharge  des
opérateurs   classiques   (comme   +   qui  peut
désigner l'addition de nombres, de fonctions, de
vecteurs...    et    la    concaténation     des
chaînes  de  caractères)  permet  une plus
grande   lisibilité   et   un   meilleur  niveau
d'abstraction.    L'envoi  de  messages  se   substitue
à  l'écriture  d'expression,  l'appel  de
sous  programmes.    Les  instructions  traditionnelles
argument_gauche   opérateurO   argument_droiteet
appeldu sousprogramme  X avec  l'argument A  deviennent
respectivement  l'envoi  du  message  opérateurO
argument_droite  à  l'objet  argument_gauche  et
l'envoi  du   message  X   à  l'objet   A.  Bien
qu'artificiel parfois  (comment penser  que dans  2 + 3
les  nombres  2  et  3  ne  jouent  pas  un  rôle
symétrique  mais  qu'il  y  un  le  message  + 3
envoyé  à  2  ?)  ce  mécanisme de
transmission de message est très souple.
3.3.  Conception par objets
De  même   de  la   programation  modulaire,   le
découpage  en  fichiers  logiques  et  physiques
(fonction,   procédures,sous   programmes,    et
fichiers inclus) représentent un  progrès
par  rapport  à  la  programmation  monobloc, la
programmation  par  objets  permet  un  meilleur niveau
d'abstraction.    L'abstraction  provient  tout d'abord
d'une  simplification  ou  unification  :    au lieu de
manipuler des données  et des programmes,  on ne
traite  qu'un  seul  type  d'entité, les objets.
Ensuite, il y a un  seul moyen de communication :   les
messages.    Enfin,  le  raffinement  progressif   dans
l'élaboration   des   solutions   se   font  par
l'écriture  de  sous-classes  ou de sur-classes.
La conception  d'une application  orientée objet
repose  sur  la  construction  d'une  hiérarchie
d'objets  exprimant  la  "généalogie" des
objets  de  l'application.     On  se   méfiera,
toutefois   de   la   dépendance   du    langage
sous-jacent  :     en   PO,  la   gestion  des   objets
diffère beaucoup de celle de la POO.  Ainsi,  un
objet pour  Turbo Pascal  Objet ressemble  à une
structure  de  type  ENREGISTREMENT,  qui  n'est jamais
qu'une     façon     parmi     tant     d'autres
d'implémenter les objets.
Le    schéma    de    développement    en
programmation classique passe  par une phase  d'analyse
où les actions sont  privilégiées.
Les données et les structures de  données
ne  sont  intégrées  qu'en  tout dernier.
Ainsi,  l'algorithme  focalise  la  lecture d'un nombre
alors que l'implémentation et la traduction dans
un  langage  typé  viendra spécifier s'il
s'agit d'un  entier, d'un  réel etc.  même
s'il  est  difficile  de  justifier  à priori le
choix (comment  choisir entre  le type  real, single et
complex en  Turbo Pascal  ?).   En PO,  on se concentre
d'abord  sur  les  classes  d'objets,  puis  sur  leurs
caractéristiques  et  enfin  sur  les   actions.
Cette  programmation  permet  de  mélanger  plus
intimement algorithme et données, d'examiner les
rapports entre code et valeur.  On viendra par  exemple
dans  le   cas  de   saisies  multiples   recenser  les
différents  types   d'objets  à   saisir,
choisir quelles vérifications on imposera  avant
de détailler chaque classe.
4. Exemple de PO pour le problème de Comar
4.1.  Vocabulaire de la PO
Le  vocabulaire  traditionnel  de  la  PO  comprend les
termes   de   Classes,   d'Objets,   de   Champs,    de
Méthodes,  de  Messages  et  d'Instances.    Ces
différentes    notions    peuvent     être
présentées dans  la résolution  en
PO  du  problème  de  COMAR.   Une classe est la
définition  "théorique"  de  l'objet, son
moule,   sa   "déclaration"   au   sens    d'une
énumération  de  ses possibilités.
Ainsi la  classe Phrase  est caractérisée
par sa  longueur, son  texte.   Les "opérations"
sur  Phrase  sont  la  lecture,  la noelisation...  Les
champs    d'un    objet    sont    les   données
associées,   les   méthodes   sont    les
programmes associés.   Ainsi  longueur et  texte
sont des champs de  Phrase, lecture et noelisation  des
méthodes   de   Phrase   (mais   on   aurait  pu
définir longueur comme une méthode).  Les
objets  comuniquent   entre  eux   et  avec   le  monde
extérieur par message.  L'envoi d'un message est
donc similaire à l'appel d'une procédure,
d'une fonction.  Une  instance de la classe  Phrase est
la  réalisation  effective  d'un  objet  de type
Phrase.     La  création   d'une  instance   est
différente du  remplissage des  champs de  cette
instance qui se fait soit par héritage soit  par
messages.
4.2.  Application au problème de Comar
Une   instance   de    la   classe   Phrase    est   la
réalisation effective d'un objet de type Phrase.
La  création  d'une  instance  peut   être
différente du  remplissage des  champs de  cette
instance qui se fait soit par héritage soit  par
messages.  Ainsi,  le remplissage des  chamlps longueur
et   texte   d'un   objet   Phrase   sera  certainement
réalisé  par  la  méthode Lecture.
Dans le problème  de Comar, la  donnée de
départ  est  une  PHRASE  composée   d'un
ensemble de MOTS qu'il faut DENOMBRER, INVERSER et  qui
doit ensuite être  RETRANSCRITE en arbre  de noël
avec pour  sommet son  MILIEU.   A priori,  deux OBJETS
émergent immédiatement :  l'objet  PHRASE
et l'objet MOT qui héritera de ses CHAMPS et  de
ses  METHODES.    L'objet  PHRASE  aura  notamment pour
CHAMPS sa  longueur (nombre  de caractères),  sa
cardinalité (nombre de mots) et pour METHODES la
lecture,  la  recherche  du  milieu,  la  "Noëlisation"
(écriture   en   arbre   de  noel),  l'inversion
(retournement).      L'objet   MOT  héritera  de
longueur,   de   lecture   et   d'inverse.      Si   on
préfere,  on  puet  ne  créer qu'un objet
(Chaine) dont  Phrase et  Mot sont  des instances,  car
finalement,      un      mot      peut       être
considérée  comme  une  phrase.   On peut
généraliser le  problème de  Comar
en  demandant  d'afficher  en  arbre  de  Noël soit une
phrase, soit un mot, soit un vecteur (liste de nombres)
ou une  liste de  symboles Dans  tous ces  cas, on doit
décomposer        une        collection       en
éléments et la réécrire  en
arbre  de  noel.     On  peut  donc  créer   une
sur-classe de Phrase nommée Collection dont  les
champs    sont     sa    cardinalité     (nombre
d'éléments)            et             ses
éléments, dont  des méthodes  sont
lecture,  noelisation,  inversion.    C'est à ce
niveau que la  PO prend tout  son sens :   le temps  de
développement passé à rajouter une
nouvelle  collection   est  très   réduit
puisqu'on n'aura pas  à rechercher les  sources,
à réécrire ou à recopier en
modifiant légèrement les  entrées,
les sorties.
Une   présentation   simplifiée   de   la
hiérarchie des objets correspond à
Collection
    Phrase
       Mot
    Vecteur
       Nombre
         Entier
         Rationnel
...
    Liste
       Symbole
etc.
Le graphe d'héritage complet associé  est
alors :
    Objet Collection Champ   Cardinalité
    +                Champ   Eléments
    +                Méthode Lecture
    +                Méthode Noelisation
    +                Méthode Inversion
    +-----Objet Phrase Champ   Cardinalité (hérité)
    +     +            Champ   Eléments (hérité)
    +     +            Champ   Longueur (propre)
    +     +            Méthode Lecture (héritée)
    +     +            Méthode Noelisation (héritée)
    +     +            Méthode Inversion (héritée)
    +     +-----Objet Mots Champ   Longueur (hérité)
    +                      Champ   Contexte (propre)
    +                      Méthode Lecture (hérité)
    +-----Objet Vecteur Champ   Cardinalité (hérité)
    +     +             Champ   Eléments (hérité)
    +     +             Méthode Lecture (héritée)
    +     +             Méthode Noelisation
    +     +             Méthode Inversion
    +     +-----Objet Nombre Champ   Valeur (propre)
    +                        Champ   Signe (propre)
    +                        Méthode Lecture (héritée)
    +-----Objet Liste Champ   Cardinalité (hérité)
    +     +           Champ   Eléments (hérité)
    +     +           Méthode Lecture (héritée)
    +     +           Méthode Noelisation
    +     +-----Objet Symbole Champ Valeur (propre)
    +                        Méthode Lecture (héritée)
etc.
Le   degré   d'abstraction   et  de  raffinement
introduit  par  la  PO  est   clair  :    au  lieu   de
définir    de    nombreuses    structures     de
données  similaires  (comme  en  Pascal)  et  de
réécrire      les       procédures
associées, la PO permet  de partir d'un type  de
structure  et  de  ses  procédures  pour ensuite
dériver  d'autres  structures,  que  ce soit des
sous-structures       ou       des      sur-structures.
Concrètement,  cela  rejoint  la  technique   de
"refinement"   (raffinement)   de   certains  types  de
programmation.    Au  fur  et  à mesure que l'on
programme, on crée ainsi des  hiérarchies
d'objets.    De   plus,  et  la   cohérence  (ou
seulement la liaison  de par le  même traitement)
entre Phrase,  Vecteur, Liste  se retrouve  grâce
à la clase Collection.  Le Pascal classique,  ne
le  permet  pas.    Ainsi,  pour  afficher une liste de
caractères  un  par  un  stockés  dans un
tableau de caractères nommé T de taille n
,  on  utilise  la  boucle  for  k  :=  1 to n do begin
writeln( T[k)  end ;  de même,  pour afficher les
différents éléments d'un tableau X
on écrit for k := 1 to n do begin writeln(  X[k)
end  ;sans  qu'il   soit  possible  de   regrouper  ces
instructions en une seule, à cause de la  nature
des  termes   utilisés.     Même  si   ces
instructions     sont     courtes,     le    même
problème  se  pose  pour  des procédures.
Ainsi, le tri  d'une liste de  nombres et le  tri d'une
liste  de  mots  ne  peut  se  faire  en Pascal avec la
même   procédure,   alors   que  la  seule
différence   se   situe   au   niveau   de    la
déclaration de l'objet trié !
En   PO,   l'héritage   permet   d'utiliser  une
métthode pour diféfrents objets,  puisque
la  notion  de  type  n'est  pas  explicité.  Si
toutefois on le désire, on peut redéfinir
à    l'intérieur    d'une    classe   une
méthode   si    l'héritage   n'est    pas
suffisant.    Par  exemple,  pour un problème de
Comar général,  on pourrait  envisager un
graphe d'héritage  bâti sur  la notion  de
collection.
4.3.  Intérêts et difficultés de la PO
Ce type  de programmation  n'est pas  pour autant  plus
facile,    plus    naturel    que    la   programmation
traditionnelle.    Tout  d'abord  il  faut   être
capable  de  structurer  la  hiérarchie,  savoir
faire dériver, hériter correctement.   La
qualité   du    logiciel   associé    est
lié à une non redondance, un masquage des
procédures les plus internes.  Avant  d'inventer
un objet,  il faut  donc vérifier  qu'il n'a pas
déjà  été   inventé,
comme  en  programmation  classique.    Avec un langage
comme Smalltalk, il y a en standard environ 100  objets
et    2000    méthodes.        S'y     retrouver
représente  un  gros  travail.   Par exemple, il
serait incohérent de réinventer le calcul
de la  moyenne d'une  liste de  nombres si  elle existe
déjà.    Parcourir  l'encyclopédie
des classes (partie 4 du manuel de Smalltalk) où
utiliser en interactif le Class Hierarchy Browser prend
beaucoup   plus   de   temps   que  d'écrire  la
méthode nommée moyenne pour l'objet liste
de nombre.  Le masquage des procédures donne une
programmation plus aisée  :  on  travaille comme
avec une bibliothèque de sous programmes dont on
ne  connait  que  la  syntaxe  d'appel,  les conditions
d'utilisations,  mais  pas  le  texte  source.  Cela ne
garantit pas pour autant de la "bonne"  écriture
de ces procédures.  Un mauvais programmeur  fera
un piètre programmes en PO.  Prenons un  exemple
concret :  supposons que nous redéfinissions  la
conversion    en    majuscule    d'une    chaine     de
caractères par programme, sachant qu'on  dispose
d'une conversion caractère par  caractère
seulement.  Nous voulons utiliser pour cela une  boucle
REPETER ...   JUSQU'A.   Notre  idée (fausse) de
départ    est    qu'on    doit    traiter    les
caractères    1    à    n   où   n
désigne  la  longueur  de  la  chaine.  Pour une
chaine  nommée  CH  l'algorithme itératif
associé est
N * longueur(CH)
{ on parcourt la chaine caractère par caractère
  noté K et repéré par son indice IC }
IC * 1
répéter
   K * SousElément(CH,IC)
   ... { conversion de K dans CH }
   IC * IC + 1
jusqu'à IC = N
L'erreur de notre méthode est de ne pas  prendre
en  compte  la  chaîne  vide.    Faire  de  notre
algorithme  une  fonction  ou  une  procédure ne
résoudra pas  le problème,  pas plus  que
d'en   faire   une   méthode   pour  les  objets
associés aux chaines de caractères.   Une
autre difficulté  de la  PO qui  ne la  rend pas
immédiatement  accessible  est  qu'elle ne prend
tout  son  sens  sert  que  pour des "gros" programmes.
Pour  un  programme  court  et  jetable  (et  il y en a
beaucoup), la PO n'est pas forcément rentable.
Par   contre,   des   environnements   complets   (tels
Smalltalk)  présentent  un  "plus"  par  rapport
à  des  langages  orientés  objets  :  la
diminution  du  nombre  de  fichiers,  par exemple, est
flagrante.    Smalltalk  contient  tout  en  une  seule
"image".    On  est  loin  des  fichiers  sources,  des
fichiers inclus,  des fichiers  de données,  des
fichiers  de  code  relogeable,  des  fichiers  de code
exécutable etc.
4.4 Langages :
Quatre     langages     différents     viendront
détailler  les  différences  entre  PO et
POO, à savoir :  Smalltalk, Lisp/Flavors,  Turbo
Pascal Objet et  C++.  Plus  qu'un exercie de  style ou
d'école,  le  passage  par  ces  divers langages
viendra montrer les pièges et les oppositions de
concepts,  de  développement.    On  s'attachera
donc,  après  l'écriture  des  programmes
à une  comparaison critique.   SMALLTALK  est le
langage objet par excellence car tout n'y est qu'objet.
Smalltalk est écrit en Smalltalk (sauf le  noyau
écrit  en  assembleur).    Il intègre les
objets   et   leur    environnement   dans   un    tout
homogène  et  graphique.    Le  passage  par  ce
langage de  référence est  obligé.
LISP/FLAVORS est aussi un standard.   Si la PO et  l'IA
sont liés, ce langage en montrera les  liaisons,
les apports réciproques.
TURBO     PASCAL     Orienté     Objets      est
dérivé  de  Pascal,  langage  très
connu en milieu universitaire.  L'utiliser permettra de
dégager les bienfaits ou les problèmes de
la POO.  C++ est une référence en  termes
de   développement   industriel   (ou  au  moins
posé comme tel).
5. Le langage Smalltalk
Un exemple simple de programme Smalltalk est celui  qui
correspond  à  un  dialogue  de  "bonjour".   Le
programme  affiche  Bonjour  sur  l'écran,  puis
demande un  prénom.   Il affiche  ensuite sur la
ligne suivante la date et l'heure puis écrit "au
revoir"  suivi  du  nom  de  la personne en majuscules.
Exemple d'exécution :  si monsieur TEST lance le
programme à 11 h 30 :  12 le 12/01/1992, on aura
sur l'écran :
Bonjour  Quel  est  votre  prénom  ?  Test Le 12
janvier  1992  à   11:30  ;  au   revoir,  TEST.
L'algorithme  correspondant  ne  présente aucune
difficulté.  Il fait référence aux
fonctions  date,   heure  et   maju.     On  aurait  pu
écrire  date()  et  heure()  au  lieu de date et
heure,  MCRIRE  au  lieu  de  MECRIRE  (qui   signifie,
rappelons "écrire sur  la même ligne  sans
passer à la ligne suivante") pour garder  autant
de lettres que dans le mot ECRIRE.
{ Algorithme de BONJOUR }
 ECRIRE  "Bonjour."
 MECRIRE "Quel est votre prénom ?
 "  LIRE    pren
 ECRIRE  "Le ",date," à ",heure," au revoir, ",maju(pren)
L'exemple un met en jeu la notion d'entrée et de
sortie,    de    conversion    en    majuscules,     de
récupération de  la date  et de  l'heure.
En Smalltalk, on écrit :
 bonjour  " Exemple de commentaire, lire, écrire et affectation "
 | pren |
  Cursor offset: 140 @ 150.
  pren := Prompter   prompt: ' Bonjour. Quel est ton prénom ? '
                     default: ''.
  pren := ' Merci. Au revoir ' ,
             (pren asUpperCase), '' ,
             (Date today) printString.
  Cursor offset: 140 @ 300.
  Menu message: pren.
  " fin de bonjour "
Ce  programme  appelle  quelques  commentaires.     Les
programmes  sont  écrits  en  format libre, mais
avec  respect  des  majuscules  et  minuscules pour les
identificateurs.          Les     instructions     sont
séparées par des points (et l'indentation
les  met  en   évidence).    Les   variables  en
majuscules   correspondent   à   des   variables
globales ; certaines méthodes  ont un :   en fin
pour indiquer qu'il  faut des paramètres  ; pren
est  une  variable  locale  (indiqué par les |).
Ainsi Cursor, Prompter,  Date, Menu sont  des variables
globales,  offset:,  prompt:,  default:,   asUpperCase,
today, printString, message:  sont des méthodes.
Smalltalk  travaille   uniquement  en   mode  graphique
fenêtré.      Pour   avoir  le  même
affichage à chaque exécution, on  indique
une   position   curseur   (sinon,   l'affichage    est
effectué en fonction de la position courante  de
la souris).  La  demande de pren est  accompagné
d'une valeur  par défaut,  ce qui  est une bonne
chose.  ECRIRE puis MCRIRE de l'algorithme ne sont  pas
traduits.      Il    est   en   effet    difficile   de
réécrire    de    façon     simple
(c'est-à-dire   sans   passer   par  les  objets
d'entrées:sorties de type "stream") à  la
suite  du  dernier  texte  écrit.    La  fin  du
programme par Menu permet d'attendre une réponse
utilisateur.  Sinon, comme  en Pascal, le programme  se
termine et l'affichage disparait.  Pour sauvegarder  ce
programme (qu'on a pu écrire dans le Transcript,
par  exemple),  il  faut  l'associer à un objet.
Ici, par exemple, on pourra en faire une méthode
d'un  objet  "démonstration".    Le  nom  de  la
méthode  sera  bonjour.    On  mettra  donc   en
en-tête :
bonjour  " Exemple de commentaire, lire, écrire et affectation "
Les commentaires  entre "  sont facultatifs.   Une fois
l'ensemble  du   texte  sélectionné,   on
appelle le "Browser" qui gère les objets et leur
métodes.    On  se  positionne  sur  la   classe
Démo et  utilise l'option  File du  menu.   Pour
réutiliser  la  méthode  Bonjour, dans le
Transcript,   on   ne    peut   taper   bonjour    puis
l'exécuter car en objet, tout message a un objet
récepteur  (seuls   les  paramètre   sont
facultatifs).   Il faut  donc créer  un objet de
type démo (ou en retrouver un).  Dans le premier
cas, on exécute
 | tstDemo |
 tstDemo := DemoClass new.
 tstDemo bonjour
Dans le second cas, on cherche avec le dictionnaire (en
exécutant  Smalltalk  inspect  et  en descendant
dans la liste  des variables) un  objet global de  type
demo.  On trouve par exemple Demo DemoGil Gil.  On peut
alors exécuter
 Demo bonjour
Pour connaitre la classe  d'un objet, par exemple  pour
savoir comment 2 est reconnu, il suffit de taper
2 class
Le système répond alors :   SmallInteger.
De même, SmallTalk est de type SystemDictionnary.
On peut parcourir une  classe par edit et  une variable
par inspect.  Ainsi, on peut taper
 2 inspect
 Smalltalk inspect
 DemoClass edit.
Comme  deuxième  exemple,  prenons  la  table de
multiplication.  L'algorithme est :
ECRIRE " De quel nombre voulez-vous la table ? "
LIRE nb
ECRIRE " Table de multiplication de " , nb
POUR indr  DE1A 10
   produit <-- indr  * nb
   ECRIRE indr , " fois " nb , " = " , produit
FIN_POUR indr { DE1A 10 }
L'écriture  en  Smalltalk  se  fait en plusieurs
temps :  on écrit le module principal comme  une
méthode   de   l'objet   démo,   soit  le
programme :
tabmult  " Table de multiplication "
   | nb  |
   nb := Prompter prompt: 'Donner un entier de 1 à 10 '
                  default: '3'.
(nb asInteger) table.!
" fin de tabmult "
Ce programme fait appel au module table.  Celui-ci  est
une méthode associée à un objet de
type integer.   On  peut donc  exécuter dans  le
Transcript,    indépendamment    du    programme
précédent  l'instruction  "4  table".  Le
détail de table suit.
table  " de multiplication "
   | fenetre  |
   fenetre := TextEditor windowLabeled: 'Table de Multiplication '
             frame: (210 @ 30 extent: 400 @(Display extent //2)y).
 1 to: 10 do: [ :indr | fenetre nextPutAll: indr format ;
                 nextPutAll: ' fois ' ;
                 nextPutAll: self printString ;
                 nextPutAll: ' = ' ;
                 nextPutAll: (indr * self) format ;
                 cr.
               ].
fenetre activate.
Menu message: 'Cliquez en gauche ici pour terminer'.
" fin de table "
La  méthode  table  utilise  une  méthode
nommée  format.     Nous   avons  volontairement
compliqué  format  puisqu'elle  apelle   format:
(ceci pour montrer  le passage de  paramètres et
la  variable  self).    Le  symbole  ^  indique  que la
méthode    renvoie    le    résultat   de
l'instruction   qui   suit   le   symbole  ^  (à
l'esclusion des commentaires).
format " Formate sur 3 caractères un entier "
^ self format: 3 " fin de format "
format: nbdeCar  " Formate un entier sur nbdeCar caractères  "
   | chaine |
   chaine :=  self printString.
   1 to: (nbdeCar - (chaine size)) do: [ :ind | chaine := ' ' , chaine ].
  ^chaine " fin de format: "
Il  faut  remarquer   que  format,  format:     peuvent
être   utilisées   par   n'importe    quel
programme,  n'importe   quelle  méthode.     Les
méthodes sont donc  des modules dynamiques,  par
raport aux modules des bibliothèques classiques.
De plus,  elles ne  sont pas  liées à  un
fichier particulier :  elles font partie de  Smalltalk.
L'ensemble   de   Smalltalk   est   donc   ouvert    et
sauvegardé dans un  fichier image (qui  est donc
gros - de l'ordre du Méga).  Le Transcript aussi
et     toutes     les     interactions    sont    aussi
sauvegardées,  ce  qui  permet de défaire
tout ce qui a été fait, de retrouver  des
essais,  des  valeurs...    Il  est hors de question de
présenter les 100 objets et 2000 méthodes
(environ) disponibles en standard avec Smalltalk.