Maitrise d'Informatique
Module POD
Programmation Objet et Distribution
Université Pierre et Marie Curie
Année 2000--2001



Partiel du 4 décembre 2000
Notes manuscrites et polycopiés autorisés - Durée 2 heures

1   Exercice 1 : conjecture de Syracuse

Soit la suite suivante  : " x Î N*
u0 = x
un =
ì
í
î
1 si un-1 vaut 1
un-1/2 si un-1 est pair
3*un-1+1 si un-1 est impair
la conjecture de Syracuse indique que cette suite converge toujours vers 1. On appelle altitude maximale la plus grande valeur trouvée pour un un et vol en altitude le nombre de un de valeur plus grande que le u0 initial.
On définit la classe syrac suivante qui calcule les différents ui jusqu'à la convergence vers 1.

source inférence de types
class syrac x = 
object(self)
  val mutable u_0 = x
  val mutable u_n = x 
  val mutable duree = 0
  method get_u_0 = u_0
  method set_u_0 u = u_0 <- u
  method get_duree = duree
  method set_duree d = duree <- d
  method private une_etape () = 
      if u_n mod 2 = 0 
      then u_n <- u_n / 2
      else u_n <- 3 * u_n + 1
  method calcule () = 
    if u_n = 1 then () 
    else 
    begin
      self#une_etape();
      duree <- duree + 1;
      self#calcule()  
    end
end;;
class syrac :
  int ->
  object
    val mutable duree : int
    val mutable u_0 : int
    val mutable u_n : int
    method calcule : unit -> unit
    method get_duree : int
    method get_u_0 : int
    method set_duree : int -> unit
    method set_u_0 : int -> unit
    method private une_etape : unit -> unit
  end
  1. Indiquer les différentes valeurs prises par la variable d'instance u_n de l'objet x dans le programme suivant :
    # let x = new syrac 7;;
    val x : syrac = <obj>
    # x#calcule();;
    - : unit = ()
    # x#get_duree;;
    - : int = 16
    
    Solution 1.1
    7
    22
    11
    34
    17
    52
    26
    13
    40
    20
    10
    5
    16
    8
    4
    2
    1
    


  2. On cherche à conserver la plus haute valeur atteinte (altitude maximale) ainsi que le nombre de valeurs au dessus du nombre initial (vol en altitude). Ecrire une sous-classe syrac_plus de syrac qui permet de conserver ces valeurs et d'y avoir accès à la fin d'un calcul par les méthodes get_alt et get_vol.

    Solution 1.2
    fichier : q12.ml
    class syrac_plus x = 
    object(self)
    inherit syrac x as super
      val mutable vol =  0
      val mutable alt = 0
      method get_vol = vol
      method set_vol v = vol <- v
      method get_alt = alt
      method set_alt a = alt <- a
      method calcule() = 
        if u_n = 1 then () 
        else 
        begin
          self#une_etape();
          duree <- duree + 1;
          if alt < u_n then alt <- u_n;
          if u_n > u_0 then vol <- vol + 1; 
          self#calcule()  
        end
    end;;
    
    
    
    


  3. Que retournent alors les appels suivants :
    let x = syrac_plus 7;; 
    x#get_duree;; 
    x#get_alt;; 
    x#get_vol;;
    
    Solution 1.3
    (* Attention erreur d'énoncé signalée pendant le partiel : 
    
       il faut remplacer la 1ere ligne par les 2 suivantes :
    
    
      let x = new syrac_plus 7;;
      x#calcule();;
    
     *)
    
    # let x = new syrac_plus 7;;
    val x : syrac_plus = <obj>
    # x#calcule();;
    - : unit = ()
    # x#get_duree;; 
    - : int = 16
    # x#get_alt;; 
    - : int = 52
    # x#get_vol;;
    - : int = 12
    
    
    

Exercice 2 : Observateurs

Le but de cet exercice est d'implanter le motif de conception (design pattern) ``observateur''.
Soit le programme suivant :
source inférence de types
class virtual obs_abs () = 
object
  method virtual miseajour : unit -> unit
end;;

class  sujet_gen () = 
object
  val mutable lobs = ([] : obs_abs list)
  method  attache o = lobs <- o :: lobs
  method  detache o = 
    lobs <- List.filter (fun x -> x <> o) lobs
  method  private notify () = 
     List.iter (fun x -> x#miseajour()) lobs
end;;

class ['a] sujet (x:'a) = 
object
  inherit sujet_gen () 
  val mutable etat = x
  method acqEtat  = etat 
end;;

class ['a] obs (x : 'a sujet) = 
object(self)
  inherit obs_abs ()
  val s = x
  val mutable etat = x#acqEtat
  initializer  s#attache(self :> obs_abs)
  method miseajour () = etat <- s#acqEtat
end;;

let s = new sujet (0.0, 0.0);;
let o1 = new obs s;;
let o2 = new obs s;;
s#attache(o1);;
s#attache(o2);;
class virtual obs_abs :
  unit -> 
  object 
    method virtual miseajour : unit -> unit 
  end
class sujet_gen :
  unit ->
  object
    val mutable lobs : obs_abs list
    method attache : obs_abs -> unit
    method detache : obs_abs -> unit
    method private notify : unit -> unit
  end
class ['a] sujet :
  'a ->
  object
    val mutable etat : 'a
    val mutable lobs : obs_abs list
    method acqEtat : 'a
    method attache : obs_abs -> unit
    method detache : obs_abs -> unit
    method private notify : unit -> unit
  end
class ['a] obs :
  'a sujet ->
  object
    val mutable etat : 'a
    val s : 'a sujet
    method miseajour : unit -> unit
  end
val s : (float * float) sujet
val o1 : (float * float) obs
val o2 : (float * float) obs
  1. Ecrire le schéma des relations (agrégation et héritage) entre ces quatre classes.
  2. Si la méthode notify n'était pas private, quelles seraient les actions déclenchées par l'appel suivant : s#notify()?
  3. Soient les classes concrètes sujet_syrac et obs_syrac :
    source inférence de types
    class sujet_syrac a b  = 
    object(self)
      inherit 
        [syrac_plus] sujet (new syrac_plus a) 
        as super
      val mutable debut = a
      val fin = b
      method run () =
        while debut <= fin do
          print_int debut; print_newline();
          etat <- new syrac_plus debut;
          etat#calcule();
          self#notify();
          debut <- debut + 1
        done    
    end;;
    
    class virtual obs_syrac s = 
    object(self)
      inherit [syrac_plus] obs s as super
      method miseajour () = 
        if self#check() then 
          begin 
             super#miseajour();
             self#display()
          end
      method virtual check : unit -> bool 
      method virtual display : unit -> unit 
    end;;
    
    class sujet_syrac :
      int ->
      int ->
      object
        val mutable debut : int
        val mutable etat : syrac_plus
        val fin : int
        val mutable lobs : obs_abs list
        method acqEtat : syrac_plus
        method attache : obs_abs -> unit
        method detache : obs_abs -> unit
        method private notify : unit -> unit
        method run : unit -> unit
      end
    
    class virtual obs_syrac :
      syrac_plus sujet ->
      object
        val mutable etat : syrac_plus
        val s : syrac_plus sujet
        method virtual check : unit -> bool
        method virtual display : unit -> unit
        method miseajour : unit -> unit
      end
    
    attacher ces classes au schéma de relations de la question 1.

  4. Ecrire trois classes concrètes obs_duree, obs_vol et obs_alt héritant de la classe obs_syrac qui mettent à jour leur état interne si l'état passé par le sujet est plus grand respectivement pour la durée, le vol et l'altitude. En cas de changement d'état interne d'une instance de ces classes, il y a un affichage du nombre intitial et de la valeur qui a provoqué ce changement.

    Solution 2.4
    class obs_duree s = 
    object
      inherit obs_syrac s as super
      method check () = 
       etat#get_duree < s#acqEtat#get_duree
      method display () = 
       print_string "durée de ";
       print_int etat#get_u_0; print_string " = ";
       print_int etat#get_duree;
       print_newline()
    end
    
    class obs_vol s = 
    object
      inherit obs_syrac s as super
      method check () = 
       etat#get_vol < s#acqEtat#get_vol
      method display () = 
       print_string "vol de ";
       print_int etat#get_u_0; print_string " = ";
       print_int etat#get_vol;
       print_newline()
    end
    
    class obs_alt s = 
    object
      inherit obs_syrac s as super
      method check () = 
       etat#get_alt < s#acqEtat#get_alt
      method display () = 
       print_string "altitude de ";
       print_int etat#get_u_0; print_string " = ";
       print_int etat#get_alt;
       print_newline()
    end
    


  5. Qu'affiche l'exécution du programme suivant :
    let sx = new sujet_syrac 1 8;;
    let od = new obs_duree (sx :> syrac_plus sujet);; 
    let ov = new obs_vol (sx :> syrac_plus sujet);;  
    let oa = new obs_alt (sx :> syrac_plus sujet);;  
    sx#run();;
    
    Solution 2.5
    # let sx = new sujet_syrac 1 8;;
    val sx : sujet_syrac = <obj>
    # let od = new obs_duree (sx :> syrac_plus sujet);;
    val od : obs_duree = <obj>
    # let ov = new obs_vol (sx :> syrac_plus sujet);;  
    val ov : obs_vol = <obj>
    # let oa = new obs_alt (sx :> syrac_plus sujet);;  
    val oa : obs_alt = <obj>
    # sx#run();;
    1
    2
    altitude de 2 = 1
    durée de 2 = 1
    3
    altitude de 3 = 16
    vol de 3 = 5
    durée de 3 = 7
    4
    5
    6
    durée de 6 = 8
    7
    altitude de 7 = 52
    vol de 7 = 12
    durée de 7 = 16
    8
    - : unit = ()
    


  6. Dans la classe obs expliquez la contrainte de type dans l'initialiseur.

Exercice 3 : Observateurs distants

Dans ce dernier exercice, on cherche à implanter des observateurs répartis. Ceux-ci communiquent avec le sujet via des sockets TCP/IP selon un schéma de clients (observateurs) et serveur(sujet).

Le protocole utilisé est simple. La connexion d'un client au serveur est considérée comme un attachement et la fermeture de la socket par le client comme un détachement de la liste des observateurs. La communication sur la socket correspond à une mise à jour et la valeur de l'état transite alors sur la socket dans le sens serveur vers client.

Le serveur doit effectuer 2 tâches : être à l'écoute de connexions de possibles clients et effectuer un certain travail.

En parallèle les clients effectuent aussi 2 tâches : être à l'écoute de l'envoi d'un changement d'état de la valeur observée sur le serveur et effectuer un certain travail.

On se donne les deux classes abstraites suivantes : serv_gen et client_gen :

classe serv_gen classe client_gen
let get_my_addr () =
  (Unix.gethostbyname(Unix.gethostname())).Unix.h_addr_list.(0) ;;

class virtual serv_gen p = 
  object (self)
    val port = p 
    val mutable sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0
    val mutable id_ecoute = Thread.self ()
    val mutable id_travaille = Thread.self ()

    initializer 
      let mon_adresse = get_my_addr () 
      in Unix.bind sock (Unix.ADDR_INET(mon_adresse,port)) ;
         Unix.listen sock 3
          
    method private client_addr = function 
      Unix.ADDR_INET(host,_) -> Unix.string_of_inet_addr host
    | _ -> "Unexpected client"
       
    method virtual ecoute : unit -> unit
    method virtual travaille : unit -> unit 
    method start () = 
      id_ecoute <- Thread.create ( fun () -> self#ecoute()) ();
    print_string "ATTENTE "; print_newline();
    Thread.delay(30.0);
      self#travaille()
    method stop() = 
      Thread.kill id_ecoute;
      exit 0; ()
end ;;
class virtual client_gen m p =
object(self)
  val port = p
  val serveur = m
  val mutable adr_serv =  
      (Unix.gethostbyname m).Unix.h_addr_list.(0)
  val mutable sock = 
    ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0
  val mutable id_ecoute = Thread.self ()
  val mutable id_travaille = Thread.self ()

  initializer  
    ThreadUnix.connect sock (Unix.ADDR_INET(adr_serv, port))

  method virtual ecoute : unit -> unit
  method virtual travaille : unit -> unit

  method start () = 
    id_ecoute <- Thread.create ( fun () -> self#ecoute()) ();
    self#travaille()
  
  method stop() = 
    Thread.kill id_ecoute;
    exit 0; ()
end ;;

ainsi que la classe abstraite com_gen pour la communication entre serveur et client et enfin les classes sujet_serveur et syrac_comc:

classe com_gen classes sujet_serveur et com_syrac
class virtual  com_gen (sd : Unix.file_descr) = 
object(self:'a)
  method virtual to_string : unit -> string
  method virtual of_string : string -> unit 
  method miseajour () = self#send()
  method  send () = 
    let message = self#to_string() in 
    let lm = String.length message in 
    let lms = (string_of_int lm)^"\n" in 
      ThreadUnix.write sd lms 0 (String.length lms); 
      ThreadUnix.write sd message 0 lm;
      () 
  method receive () = 
    let buffer = String.create 1 in
    let r = ref "" in  
    while (buffer <> "\n") do 
      ThreadUnix.read sd buffer 0 1;
      r := !r ^ buffer 
    done;
    let l = int_of_string !r in 
    let str = String.create l in 
      ThreadUnix.read sd str 0 l;
      self#of_string str
end;;
class virtual ['a] sujet_serveur_abs (x :'a) p = 
object 
  inherit serv_gen p as serv_super
  val mutable etat = x
  val mutable lobs = ([] : com_gen list)
  method getEtat = etat
  method  attache o = lobs <- o :: lobs
  method  detache o = 
    lobs <- List.filter (fun x -> x <> o) lobs
  method  private notify () =
    List.iter (fun x -> x#send()) lobs
end;;

class obs_com_syrac sd (x:syrac_plus sujet_serveur_abs) = 
object(self)
  inherit  com_gen sd as super
  val s = x
  val e = x#getEtat
  initializer s#attache(self :> com_gen)
  method to_string () = 
    Marshal.to_string (e#get_u_0,e#get_duree,
                       e#get_alt,e#get_vol) []
  method of_string ms = 
    let n,d,a,v = Marshal.from_string ms 0 in 
      e#set_u_0(n);
      e#set_duree(d);
      e#set_alt(a);
      e#set_vol(v)
end;;

  1. Indiquer les relations entre les classes définies précédemment.
  2. Ecrire la classe concrète sujet_serveur_syrac qui lance le calcul de la suite sur un intervalle d'entiers et envoie à chaque étape les valeurs des variables d'instance aux clients enregistrés.

    Solution 3.2
    class sujet_serveur_syrac a b p = 
    object(self:'a)
      inherit  [syrac_plus] sujet_serveur_abs (new  syrac_plus a) p  as super
      val mutable debut = a
      val fin = b
      method ecoute () =  
        while(true) do 
          let (sd,sa) = ThreadUnix.accept sock in 
            new obs_com_syrac sd  (self :> syrac_plus sujet_serveur_abs)
        done
      method travaille () = 
        while debut <= fin do
          print_int debut; print_newline();
          etat <- new syrac_plus debut;
          etat#calcule();
          self#notify();
          debut <- debut + 1
        done    
    
    end;;
     
    


  3. Ecrire le lancement du serveur sur le port 3000.

    Solution 3.3
    let go () = 
      let sss =   new sujet_syrac_serv 1 7 3000 in 
      sss#start();;
    
    go();;
    


  4. Ecrire la ou les classes nécessaires pour un client à l'écoute d'un serveur de la classe sujet_serveur_syrac.
  5. Indiquer ce qu'il faut ajouter à la classe sujet_serveur pour tenir compte de la déconnexion d'un client.
  6. Que se passe-t-il quand le serveur a fini son calcul?
  7. Modifier la classe client pour que la méthode travaille soit en attente de modification de changement de l'état.
  8. Indiquer comment modifier le serveur pour que la méthode calcule ne fasse son travail qu'une fois qu'il y a au moins trois clients connectés.

Ce document a été traduit de LATEX par HEVEA.