Maitrise d'Informatique
Module POD
Programmation Objet et Distribution
Université Pierre et Marie Curie
Année 2000--2001
Examen du 1er février 2001
Notes manuscrites et polycopiés autorisés - Durée 3 heures
Exercice 1 : Visiteurs
Le but de cet exercice est d'implanter le motif de conception (design pattern) ``visiteur''. Le <<Visiteur>> fait la représentation d'une opération applicable aux éléments d'une structure d'objet. Il permet de définir une nouvelle opération, sans qu'il soit nécessaire de modifier la classe des éléments sur lesquels elle agit.
On définit les classes JAVA suivantes pour les expressions arithmétiques
et leurs traitements :
Eléments |
Visiteurs |
abstract class Element {
abstract void accepteVisiteur(Visiteur v);
}
class Constante extends Element {
int cte = 0;
Constante(int n) {cte=n;}
int getval() {return cte;}
void accepteVisiteur(Visiteur v) {
v.visiteConstante(this);
}
}
class Binop extends Element {
Element fg, fd;
char oper;
Binop (Element e1, char op, Element e2)
{fg=e1; oper=op; fd=e2;}
char getop(){return oper;}
Element fg(){return fg;}
Element fd(){return fd;}
void accepteVisiteur(Visiteur v) {
v.visiteBinop(this);
}
}
|
abstract class Visiteur {
abstract void visiteConstante(Constante c);
abstract void visiteBinop(Binop bop);
}
class VisiteurAffiche extends Visiteur {
void visiteConstante(Constante c) {
System.out.print(c.getval());
}
void visiteBinop(Binop bop) {
...
}
}
class VisiteurCalcule extends Visiteur {
int result = 0;
void visiteConstante(Constante c) {
result=c.getval();
}
void visiteBinop(Binop bop) {
...
}
int getres() {return result;}
}
|
-
Ecrire l'arbre des relations d'hétitage et d'agrégation de ces classes.
- Ecrire un programme principal qui construit l'expression <<(10*10) + (10*10)>>, l'affiche et calcule sa valeur en utilisant les visiteurs.
Solution 1.2
public class Exo1 {
public static void main(String[]a) {
Element e1 = new Constante (10);
Element e2 = new Binop(e1,'*',e1);
Element e3 = new Binop(e2,'+',e2);
VisiteurAffiche v1 = new VisiteurAffiche();
VisiteurCalcule v2 = new VisiteurCalcule();
e3.accepteVisiteur(v1);
System.out.print ("=");
e3.accepteVisiteur(v2);
System.out.println(v2.getres());
}
}
- Compléter les méthodes visiteBinop de la classe VisiteurAffiche et de la classe VisiteurCalcule.
Solution 1.3
class VisiteurAffiche extends Visiteur {
void visiteConstante(Constante c) {
System.out.print(c.getval());
}
void visiteBinop(Binop bop) {
System.out.print("(");
bop.fg().accepteVisiteur(this);
System.out.print(bop.getop());
bop.fd().accepteVisiteur(this);
System.out.print(")");
}
}
class VisiteurCalcule extends Visiteur {
int result = 0;
void visiteConstante(Constante c) {
result=c.getval();
}
void visiteBinop(Binop bop) {
bop.fg().accepteVisiteur(this);
int rl = result;
bop.fd().accepteVisiteur(this);
int rd = result;
char symb = bop.getop();
switch (symb) {
case ('*') : result = rl * rd; break;
case ('+') : result = rl + rd; break;
default : throw (new RuntimeException());
}
}
int getres() {return result;}
}
- Qu'affiche alors votre programme principal?
Solution 1.4
((10*10)+(10*10))=200
- Ajouter à la hiérarchie des visiteurs un nouveau traitement qui calcule
la profondeur d'un arbre d'expressions.
Solution 1.5
class VisiteurLongueur extends Visiteur {
int result = 0;
void visiteConstante(Constante c) {
result = 0;
}
void visiteBinop(Binop bop) {
bop.fg().accepteVisiteur(this);
int rl = result;
bop.fg().accepteVisiteur(this);
int rd = result;
if (rl > rd) { result = rl + 1;}
else {result = rd + 1;}
}
int getres() {return result;}
}
public class Exo2 {
public static void main(String[]a) {
Element e1 = new Constante (10);
Element e2 = new Binop(e1,'*',e1);
Element e3 = new Binop(e2,'+',e2);
VisiteurAffiche v1 = new VisiteurAffiche();
VisiteurCalcule v2 = new VisiteurCalcule();
VisiteurLongueur v3 = new VisiteurLongueur();
e3.accepteVisiteur(v1);
System.out.print ("=");
e3.accepteVisiteur(v2);
System.out.println(v2.getres());
e3.accepteVisiteur(v3);
System.out.println("profondeur max : "+v3.getres());
}
}
- Indiquer les modifications à apporter aux visiteurs pour une nouvelle
classe Variable sous-classe d'Element.
Solution 1.6
-
Ecrire la classe Variable
- Ajouter à la classe abstraite Visiteur une méthode abstaite void visiteVariableVariable v
- Ajouter à chaque sous-classe de
Visiteur une méthode concrète
void visiteVariableVariable v
effectuant le travail correspondant.
- Quels sont les avantages et les inconvénients du motif Visiteur?
Dans quels cas l'employerez-vous?
Solution 1.7
C'est un motif de conception qui permet de différencier données et traitements.
Il est à employer pour facilement étendre les traitements sur des données qui
évolueront peu. Dans le cas contraire il peut devenir peu pratique à utiliser.
Exercice 2 : Synchronisation en RMI
Le but de cet exercice est d'implanter une synchronisation sur des objets distants. On l'illustrera cette synchronisation par un tableau d'objets.
interface Valeur extends Serializable {
String toString();
boolean inf(Valeur v);
}
class Bad_access extends Exception {};
class Bad_value extends Exception {};
interface Vecteur {
public Valeur get(int i) throws Bad_access, Bad_value;
public void set(int i, Valeur val) throws Bad_access;
public int length();
}
|
-
Définir l'interface VecteurDistant qui étend les interfaces
Remote et Vecteur.
Solution 2.1
import java.io.*;
import java.rmi.*;
interface Valeur extends Serializable {
public String toString();
public boolean inf(Valeur v);
}
class Bad_access extends Exception {};
class Bad_value extends Exception {};
public interface VecteurDistant extends Remote {
public Valeur get(int i) throws Bad_access, Bad_value, RemoteException;
public void set(int i, Valeur val) throws Bad_access, RemoteException;
public int length() throws RemoteException;
}
- Ecrire une classe VecteurD implantant
l'interface VecteurDistant.
Solution 2.2
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class VecteurD extends UnicastRemoteObject
implements VecteurDistant {
Valeur [] v = null;
VecteurD(int n) throws RemoteException {v=new Valeur[n];}
public Valeur get(int i) throws Bad_access, Bad_value, RemoteException {
if ((i<0)|| (i > v.length)) {throw (new Bad_access());}
else { if (v[i] == null) {throw (new Bad_value());}
else return (v[i]);}
}
public void set(int i, Valeur val) throws Bad_access, RemoteException {
if ((i<0)|| (i > v.length)) {throw (new Bad_access());}
else {v[i]=val;}
}
public int length() {
return v.length;
}
}
- Ecrire un serveur RMI qui enregistre sous le nom monvect
une instance de cette classe de taille 40 et où chaque élément du
vecteur contient la valeur null.
Solution 2.3
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class ServeurVecteur {
public static void main (String args[]) {
System.setSecurityManager(new RMISecurityManager());
try {
VecteurD v = new VecteurD(40);
Naming.rebind("//ippc56.infop6.jussieu.fr/monvect",v);
}
catch (Exception e) { e.printStackTrace();
}
}
}
- Ecrire un client qui affiche le contenu du vecteur distant monvect.
Solution 2.4
import java.rmi.*;
public class ClientAffiche {
public static void main( String argv[]) {
String url0="rmi://ippc56.infop6.jussieu.fr/monvect";
try {
VecteurDistant v = (VecteurDistant)Naming.lookup(url0);
int i = v.length();
for (int j=0;j<i;j++) {
System.out.print(v.get(j)+" ");
}
}
catch (Exception e) {
System.err.println("exception : " +
e.getMessage());
e.printStackTrace();
}
}
}
- Ecrire un client qui trie le vecteur distant monvect.
Solution 2.5
import java.rmi.*;
public class ClientTrie {
public static void main( String argv[]) {
String url0="rmi://ippc56.infop6.jussieu.fr/monvect";
try {
VecteurDistant v = (VecteurDistant)Naming.lookup(url0);
int i = v.length();
for (int j=0;j<i;j++) {
for (int k=0; k<j; k++) {
if (v.get(k).inf(v.get(j))) {
Valeur tmp = v.get(k);
v.set(k,v.get(j));
v.set(j,tmp);
}
}
}
}
catch (Exception e) {
System.err.println("exception : " +
e.getMessage());
e.printStackTrace();
}
}
}
- Que risque-t-il de se passer si les deux clients effectuent leur travail
silmutanément?
- Que faut-il ajouter à ce programme pour gérer l'accès concurrent
à ce vecteur distant?
- Implanter votre proposition.
Exercice 3 : Gestion d'une liste de clients
Le but de cet exercice est d'implanter la gestion d'une liste
de clients dans un serveur O'Caml.
Pour simplifier les entrées/sorties, on suppose connues les fonctions d'écriture d'une ligne et de lecture d'une ligne sur un descripteur de fichier de types suivants :
val write_line : Unix.file_descr -> string -> unit
val read_line : Unix.file_descr -> string
On se donne le squelette du serveur en O'Caml (la fonction List.nth
donne le ième élément d'une liste) :
class connexion n =
object (self : 'a )
val nmax = n
val mutable jeton = 0;
val mutable fini = false;
val mutable liste_soc = ([] : (Unix.file_descr * Unix.sockaddr) list)
method ajoute (sd,sa) =
liste_soc <- liste_soc @ [sd,sa];
write_line sd "ATTENDRE"
method get_sd i = fst(List.nth liste_soc i)
method broadcast msg =
for i = 0 to nmax-1 do
write_line (self#get_sd i) msg
done
method start() =
self#broadcast("DEBUT");
while not fini do
write_line (self#get_sd jeton) "TONTOUR";
let s = read_line (self#get_sd jeton) in
self#run(s);
jeton <- (jeton + 1) mod nmax
done;
self#close()
method run s = self#broadcast s
method close() = ()
end;;
(***********************************************************************)
class serv_socket p nmax =
object (self)
val port = p
val mutable sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0
initializer
let mon_adresse =
(Unix.gethostbyname(Unix.gethostname())).Unix.h_addr_list.(0)
in Unix.bind sock (Unix.ADDR_INET(mon_adresse,port)) ;
Unix.listen sock nmax
method private client_addr = function
Unix.ADDR_INET(host,_) -> Unix.string_of_inet_addr host
| _ -> "Unexpected client"
method run () =
while true do
let connexion = new connexion nmax in
for i=0 to nmax-1 do
let (sd,sa) = ThreadUnix.accept sock in
connexion#ajoute(sd,sa)
done;
connexion#start()
done
end ;;
partie O'Caml
- Que fait le programme suivant :
let s = new serv_socket 3124 5;;
s#run();;
- Ecrire le corps de la méthode
close
pour terminer
les connexions avec les clients.
- Sur ce modèle on veut implanter un serveur de jeu de C+/C- où les clients cherchent la valeur d'un nombre caché sur le serveur (celui-ci répond
"C+", "C-" ou "C=" selon le nombre proposé).
Implanter ce jeu en modifiant le squelette de la classe
connexion
.
- Indiquer comment faire en O'Caml pour ne pas avoir à écrire la classe
connexion avant la classe serv_socket.
partie Java
-
5. Ecrire une applet Java comme client de ce service. Cette applet possède
un champ TextField pour l'entrée d'une réponse et un champ Label
pour afficher le dernier résultat transmis. Quand le client possède le
jeton, l'action du TextField est d'envoyer la valeur entrée au serveur.
Ce document a été traduit de LATEX par
HEVEA.