 
 
 
 Relations entre classes
Les classes d'une application peuvent entretenir entre elles deux
types de relations.
- 
 Une relation d'agrégation, appelée Has-a  : 
 
 une classe C2 est dans la relation Has-a avec une classe C1 si
C2 possède un champ du type de la classe C1. Cette relation se
généralise en C2 possède au moins un champ du type de
la classe C1.
-  Une relation d'héritage, appelée Is-a :
 une classe C2 est sous-classe d'une classe C1 lorsque C2 est
obtenue par extension du comportement de C1. C'est l'avantage
majeur de la programmation par objet que de pouvoir étendre le
comportement d'une classe existante tout en continuant à utiliser le
code écrit pour la classe originale. Quand on étend une classe, la
nouvelle classe hérite de tous les champs, de données et de
méthodes, de la classe qu'elle étend.
 Relation d'agrégation
Une classe C1 est en relation d'agrégation avec une classe
C2, si au moins une de ses variables d'instance est du type de la 
classe C2. On indique l'arité de la relation quand elle est connue.
 Exemple de relation d'agrégation
On définit une figure comme un ensemble de points. On déclare
pour cela la classe picture (voir figure 15.2)
 dont un des champs est un tableau de
points. La classe picture entretient donc la relation
généralisée Has-a avec la classe point.
# class picture n =
   object
     val mutable ind = 0
     val tab = Array.create n (new point(0,0))
     method add p =  
       try  tab.(ind)<-p ; ind <- ind + 1 
       with Invalid_argument("Array.set")
               -> failwith ("picture.add:ind =" ^ (string_of_int ind))
     method remove () = if (ind > 0) then ind <-ind-1
     method to_string () = 
       let s = ref "[" 
       in for i=0 to ind-1 do s:= !s ^ " " ^ tab.(i)#to_string() done ;
          (!s) ^ "]"
   end ;;
class picture :
  int ->
  object
    val mutable ind : int
    val tab : point array
    method add : point -> unit
    method remove : unit -> unit
    method to_string : unit -> string
  end
On construit une figure en créant une instance de la classe
picture et en ajoutant les points désirés.
# let pic = new picture 8;;
val pic : picture = <obj>
# pic#add p1; pic#add p2; pic#add p3;;
- : unit = ()
# pic#to_string ();;
- : string = "[ ( 0, 0) ( 3, 4) ( 3, 0)]"
 Notation graphique de l'agrégation
La relation entre la classe picture et la classe
point est représentée graphiquement par la figure
15.2. 
Une flèche, dont l'origine comporte un losange et l'extrémité forme une pointe
vide, indique la relation d'agrégation. Dans cet exemple la classe 
picture possède entre 0 et un nombre quelconque de points. 
Figure 15.2 : relation d'agrégation
On indique par ailleurs au dessus de la flèche l'arité de la relation.
 Relation d'héritage
C'est la relation essentielle de la programmation par objet. Quand une
classe c2 hérite d'une classe c1 elle récupère tous
les champs de la classe ancêtre. Elle peut alors définir de nouveaux
champs et même redéfinir des méthodes héritées pour les spécialiser.
Comme la classe ancêtre n'a pas été modifiée, les applications s'en
servant n'ont pas à être adaptées aux changements apportés à la nouvelle
classe.
La syntaxe de la relation d'héritage est la suivante :
 Syntaxe 
 
inherit nom1 p1 ...pn
 [ as nom2 ]
Les paramètres p1, ..., pn sont ceux
attendus par le constructeur de la classe nom1. 
Il est possible d'associer un nom à la classe ancêtre pour accéder aux
méthodes de celle-ci. On utilise pour cela le mot clé as. 
Cette possibilité est particulièrement utile lorsque la classe
fille redéfinit une méthode de la classe ancêtre (voir page
??). 
 Exemple d'héritage simple
L'exemple classique est d'étendre la classe point en ajoutant
un attribut de couleur aux points. On définit alors la classe
colored_point qui hérite de la classe point. La
couleur est représentée par le champ c de type
string. On ajoute une méthode get_color qui
retourne la valeur de ce champ. La conversion vers une chaîne de
caractères doit alors tenir compte de cet attribut et sera donc
redéfinie.
# class colored_point (x,y) c =
   object
     inherit point (x,y)
     val mutable c = c
     method get_color = c
     method set_color nc = c <- nc
     method to_string () = "( " ^ (string_of_int x) ^ 
                           ", " ^ (string_of_int y) ^ ")" ^
                           " [" ^ c ^ "] "
   end ;;
class colored_point :
  int * int ->
  string ->
  object
    val mutable c : string
    val mutable x : int
    val mutable y : int
    method distance : unit -> float
    method get_color : string
    method get_x : int
    method get_y : int
    method moveto : int * int -> unit
    method rmoveto : int * int -> unit
    method set_color : string -> unit
    method to_string : unit -> string
  end
Les valeurs initiales pour la construction d'un colored_point sont le couple 
de coordonnées nécessaire pour la construction d'un point et la couleur du point 
coloré.
L'ensemble des méthodes héritées ou nouvellement définies ou
redéfinies correspond à l'ensemble des comportements des instances de la classe.
# let pc = new colored_point (2,3) "blanc";;
val pc : colored_point = <obj>
# pc#get_color;;
- : string = "blanc"
# pc#get_x;;
- : int = 2
# pc#to_string();;
- : string = "( 2, 3) [blanc] "
# pc#distance;;
- : unit -> float = <fun>
On dit que la classe point est la classe ancêtre de
la classe colored_point et que celle-ci est la classe 
fille de celle-là.
 Warning 
 
La redéfinition d'une méthode par une classe fille doit respecter le
type de la méthode définie dans la classe ancêtre.
 Notation graphique de l'héritage
La relation d'héritage entre classes se note par une flèche, dont la pointe est formée
par un triangle fermé, 
allant de la classe fille vers la classe ancêtre. Dans la
représentation graphique de l'héritage, on ne fait figurer dans
la classe fille que les nouveaux champs, les nouvelles méthodes et les
méthodes redéfinies. La figure 15.3 illustre la
relation entre la classe colored_point et son ancêtre
point. 
 
Figure 15.3 : Relation d'héritage
 
Comme il contient des méthodes supplémentaires, le type
colored_point est différent du type point.
Le test d'égalité entre deux instances de ces classes provoque l'affichage
d'un long message d'erreur qui rappelle le type complet de chaque
classe pour bien montrer leur différence. 
# p1 = pc;;
Characters 6-8:
This expression has type
  colored_point =
    < distance : unit -> float; get_color : string; get_x : int; get_y : 
      int; moveto : int * int -> unit; rmoveto : int * int -> unit;
      set_color : string -> unit; to_string : unit -> string >
but is here used with type
  point =
    < distance : unit -> float; get_x : int; get_y : int;
      moveto : int * int -> unit; rmoveto : int * int -> unit;
      to_string : unit -> string >
Only the first object type has a method get_color
 
 
