Compléments sur des concepts et des idiomes Java

Transcription

Compléments sur des concepts et des idiomes Java
Compléments sur des concepts et
des idiomes Java
D. Conan
Rev : 951
CSC 4509 — ASR4
Télécom SudParis
Juin 2013
Compléments sur des concepts et des idiomes Java
Table des matières
Compléments sur des concepts et des idiomes Java
D. Conan, , Télécom SudParis, CSC 4509 — ASR4
Juin 2013
1
Plan de la présentation
3
1 Contexte : Étude de l’application tchat multiclient et multiserveur
3
2 Architecture de l’application
2.1 Architecture des serveurs . . . . .
2.2 Architecture des clients . . . . . .
2.3 Diagramme de classes des serveurs
2.4 Diagramme de classes des clients .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4
5
6
7
9
3 Types paramétrés
10
4 Objets immuables et collections immuables
11
5 Blocs de code « static »
12
6 Type énuméré et constructeur de type énuméré
13
7 Classes anomymes
14
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
2
Compléments sur des concepts et des idiomes Java
'
$
Plan de la présentation
#2
1
2
3
4
5
6
7
Contexte : Étude de l’application tchat multiclient et multiserveur . . . . . . . . . . . . . . . . . 3
Architecture de l’application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Types paramétrés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Objets immuables et collections immuables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Blocs de code « static » . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Type énuméré et constructeur de type énuméré . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Classes anomymes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
&
%
'
$
1 Contexte : Étude de l’application tchat multiclient et
multiserveur
#3
■ Infrastucture du système de tchat multiclient et multiserveur
♦ Réalisé pendant les séances de travail personnel
♦ Mise à disposition d’une solution type par l’équipe enseignante
▶ Utilisation de concepts avancés et d’idiomes Java
⋆ Quelques compléments de cours sont fournis dans cette présentation
■ Documentation de référence :
♦ Tutoriels Java de Oracle/Sun
♦ Livre Effective Java, 2nd edition de Joshua Bloch, Addison Wesley, 2008
▶ Ce livre a été traduit en français sous le titre « Java Efficace »
&
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
%
3
Compléments sur des concepts et des idiomes Java
'
$
2 Architecture de l’application
Client 0
Client 1
Server 0
Server 1
#4
Server 2
Server 3
Client 2
Client 3
&
Client 4
%
La figure de cette diapositive présente la configuration utilisée dans nos tests avant de vous fournir cette
réalisation de l’application de tchat. Les particularités sont les suivantes :
• test de l’aspect multiclient avec deux clients attachés à certains serveurs,
• test de l’aspect multiserveur avec des serveurs connectés à plusieurs voisins,
• test de la propagation des messages de tchat avec gestion des cycles dans la topologie des serveurs.
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
4
Compléments sur des concepts et des idiomes Java
'
2 Architecture de l’application
$
2.1 Architecture des serveurs
Access to the state of the server
through ChatServerState.semaphore
#5
main thread
− infinite loop
− call to select()
and call to the
actions of the
state machine
ChatSelectorMultiServer thread
− infinite loop
− read command lines
to control the server
&
%
La figure de cette diapositive présente l’architecture d’un serveur multiactivité (en anglais, multithread).
L’activité principale du serveur est constituée d’une boucle infinie organisée autour de l’appel à la méthode
select. Une seconde activité est ajoutée pour permettre la saisie de commandes à la console afin de contrôler
le fonctionnement du serveur, par exemple lors des tests pour démarrer ou arrêter un algorithme réparti
entre les serveurs de l’application de tchat. Cette seconde activité est donc une boucle infinie de lecture de
commandes au clavier.
Dans chaque itération de la boucle infinie de l’activité principale, le serveur reçoit soit une demande de
connexion par un client ou un serveur, soit un message d’un client ou d’un serveur, puis le seveur exécute
une action de réaction à cet événement. Cette action modifie l’état du serveur et potentiellement émet des
messages vers des clients et des serveurs. La seconde activité du serveur peut aussi modifier l’état du serveur
et émettre des messages. Donc, l’état du serveur rassemblé dans la classe ChatServerState doit être accédé
en exclusion mutuelle. C’est pourquoi la classe ChatServerState contient un sémaphore qui doit être utilisé
par les deux activités concurrentes du serveur.
Par ailleurs, la forme des boucles de traitement des événements des deux activités du serveur donnent
l’indication de l’orientation naturelle utilisée pour l’insertion d’algorithmes répartis : c’est l’orientation « événement » présentée dans la section 1.2 du cours d’algorithmique répartie. Ainsi, les algorithmes répartis présentés selon l’orientation « contrôle » doivent d’abord être traduits dans une forme orientée « événement »
avant d’être insérés dans l’architecture du serveur.
L’orientation « événement » suggère l’utilisation du patron de conception « machine à états ». Aussi,
afin de faciliter l’insertion d’algorithmes répartis dans l’architecture initiale, nous ajoutons un mécanisme de
machine à états. C’est la mise en œuvre de ce patron de conception qui requiert des compléments sur des
concepts et des idiomes Java.
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
5
Compléments sur des concepts et des idiomes Java
'
2 Architecture de l’application
$
2.2 Architecture des clients
Access to the state of the client
through ChatClientState.semaphore
#6
main thread
− infinite loop
− read command lines
and send chat message
to the server
ChatClient thread
− infinite loop
− receive messages
from the server and
display them in the console
&
%
Pour complétude, la figure de cette diapositive présente l’architecture des clients. De manière similaire,
les clients sont composés de deux activités : l’activité principale pour la lecture des messages de tchat et une
seconde activité pour la réception des messages depuis le serveur. Par conséquent, comme pour le serveur,
l’état du client est accédé en exclusion mutuelle grâce à un sémaphore (cf. l’utilisation du sémaphore dans
la méthode de classe receiveChatMessageContent de la classe AlgorithmChatActions). En outre, nous
mettons en œuvre une machine à états pour insérer les algorithmes répartis entre les clients.
Notons dès maintenant qu’une des premières questions à se poser lors de l’insertion d’un algorithme
réparti dans l’architecture de l’application de tchat est de savoir si l’algorithme est à insérer dans la machine
à états des serveurs ou dans celle des clients.
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
6
Compléments sur des concepts et des idiomes Java
'
2 Architecture de l’application
$
2.3 Diagramme de classes des serveurs
#7
&
%
NB 1 : la figure est peut-être un peu trop petite pour la lecture papier. Elle est affichée à l’échelle 1 dans
le sujet en ligne.
NB 2 : le diagramme de classes est construit (semi-)automatiquement à partir du code en utilisant le
greffon Eclipse ObjectAid (http://www.objectaid.com).
Nous ne détaillons pas ici chaque classe, voire chaque attribut et chaque méthode, car nous avons documenté le code. Merci donc de vous y reporter directement ; mais, attendez la fin de l’étude de cette présentation, car nous présentons dans la suite des compléments sur des concepts et des idiomes Java. Remarquons
cependant dès maintenant les éléments de modèle suivants :
• nous retrouvons le point d’entrée du serveur (méthode ChatSelectorMultiServerMain.main) ainsi
que l’activité complémentaire (classes ChatSelectorMultiServer avec la méthode run) ;
• de nombreuses méthodes sont fournies pour la gestion des connexions et des émissions de messages.
Elles ne sont pas toutes utilisées dans l’ossature de départ, mais peuvent aider lors de l’insertion de
certains algorithmes répartis. Donc, pensez-y ;
• certaines classes sont abstraites car elles factorisent des éléments communs aux deux machines à états
(celle du seveur et celle du client) :
– la classe State est utilisée à chaque fois qu’une méthode générique (aux deux machines à états)
manipule l’état de la machine à états ;
– la classe AbstractContent est utilisée à chaque fois qu’une méthode générique manipule le contenu
d’un message ;
• l’énumération ListOfAlgorithms permet de collecter tous les algorithmes ;
• toutes les actions des algorithmes doivent respecter le contrat défini dans l’interface AlgorithmActionInterface, la méthode la plus importante étant la méthode execute qui contient l’algorithme de
l’action à exécuter. Les collections d’actions sont indexées par un entier qui est le numéro de l’action ;
• chaque algorithme est déclaré sous la forme d’une autre énumération. L’exemple fictif mis dans l’ossature est la déclaration des actions de l’algorithme d’élection dans la classe AlgorithmElection. C’est
d’ailleurs le seul algorithme déclaré pour l’instant dans la classe ListOfAlgorithms ;
• la déclaration d’un algorithme dans le second type d’énumération consiste à lister les types de messages
que la machine à états peut recevoir et à définir le code de délégation du traitement de chaque message
dans des méthodes de classe. Dans l’exemple fictif introduit dans l’ossature :
– AlgorithmElection déclare une action pour un type de messages (classe MyFirstMessageContent
non montrée dans le diagramme de classes) ;
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
7
Compléments sur des concepts et des idiomes Java
2 Architecture de l’application
– l’algorithme de l’action correspondante est la méthode de classe AlgorithmElectionActions.treatMyFirstMessage ;
• Le moteur de la machine à états correspond à la méthode de classe execute de la classe ListOfAlgorithms qui est appelée dans la boucle infinie (méthode loop) de l’activité principale du serveur
(classe ChatSelectorMultiServerMain). L’algorithme de cette méthode consiste à parcourir la liste
des actions pour trouver celle correspondant au message reçu, puis à appeler la méthode de même nom
sur l’action correspondante.
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
8
Compléments sur des concepts et des idiomes Java
'
2 Architecture de l’application
$
2.4 Diagramme de classes des clients
#8
&
%
NB : la figure est peut-être un peu trop petite pour la lecture papier. Elle est affichée à l’échelle 1 dans
le sujet en ligne.
Pour complétude, la figure de cette diapositive présente le diagramme de classes du client. Il est très
similaire à celui du serveur ; nous ne le commentons donc pas et vous redirigeons vers les commentaires
insérés dans le code.
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
9
Compléments sur des concepts et des idiomes Java
'
$
3 Types paramétrés
■ Cf. http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
#9
■ Vector<A> n’est pas un type parent de Vector<B>
♦ (new Vector< Vector<A> >()).add(new Vector<B>()) ne compile pas
■ Type joker : Vector< Vector<? extends A> >
■ Idiome utilisé dans la déclaration des attributs mapOfAlgorithms et mapOfActions
de la classe ListOfAlgorithms
■ Idiome utilisé aussi dans la déclaration de l’attribut contentClass de la classe
AlgorithmElection
&
%
Vector<A> est un collection contenant des références vers des instances de classe A ou des références vers
des instances de classe enfant de A. Mais, en supposant que B soit une classe enfant de A, Java ne permet
pas dire que Vector<B> est un sous-type de Vector<A>. Ainsi, dans un v vecteur Vector< Vector<A> >, il
n’est pas possible d’y mettre des références vers des vecteurs de type Vector<B>, car la collection v ne peut
contenir que des références vers des vecteurs de type Vector<A>.
Java utilise le caractère « ? » dans la déclaration Vector<?> (prononcée « vecteur d’inconnu » ou en
anglais « vector of unknown ») qui est le type parent de toutes les Vector. Ce type est appelé un type
« joker » (en anglais, « wildcard type »). Le même caractère est utilisé un peu différemment pour déclarer un
type joker contraint : Vector< Vector<? extends A> > : le type est contraint au sens où le vecteur peut
contenir des références vers des vecteurs contenant des références vers des instances de classe enfant de A.
C’est la signification du « ? extends A ».
Par exemple, en ayant créé auparavant la classe A et la classe B enfant de A, vous pouvez essayer le code
suivant :
Vector<Vector<A>> vva = new Vector<Vector<A>>();
Vector<A> va = new Vector<A>();
vva.add(va);
Vector<B> vb = new Vector<B>();
// vva.add(vb); KO
Vector<Vector<? extends A>> vvabis = new Vector<Vector<? extends A>>();
vvabis.add(va);
vvabis.add(vb); // OK
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
10
Compléments sur des concepts et des idiomes Java
'
$
4 Objets immuables et collections immuables
■ Cf. livre “Java Efficace” : règle 13
# 10
■ Listes immuables (non modifiables), y compris leur contenu, une fois initialisées
♦ Attribut immuable déclaré avec le mot-clef final
♦ Contenu de la collection non modifiable une fois initialisé
▶ Méthode de classe Collections.unmodifiableList
■ Idiome utilisé pour ListOfAlgorithms.mapOfAlgorithms
■ Idiome utilisé aussi pour AlgorithmElection.mapOfActions
&
%
Pour des raisons de sécurité, nous souhaitons que la liste des algorithmes dans l’énumération
ListOfAlgorithms ainsi que la liste des actions (des algorithmes) déclarée dans les seconds types d’énumération comme AlgorithmElection soient immuables (en anglais, immutable), c’est-à-dire qu’une fois
créées, les listes ainsi que leur contenu ne puissent pas être modifiés. En d’autres termes, nous souhaitons
qu’aucune méthode hors du code d’initialisation de ces énumérations ne puisse modifier les déclarations des
algorithmes.
La déclaration d’un attribut immuable en Java s’effectue en ajoutant le mot-clef final. Ainsi, la collection
mapOfAlgorithms de l’énumération ListOfALgorithms étant déclarée immuable, une fois la collection créée,
elle ne peut pas être remplacée par une autre par inadvertance ou par malignité. Mais, cela n’est pas
suffisant. En effet, nous souhaitons aussi que le contenu de la collection ne soit pas modifiable une fois qu’il
a été initialisé. Nous utilisons alors la méthode unmodifiableList de la classe Collection qui retourne
une instance de la classe UnmodifiableList, qui contraint l’utilisation de la liste en levant l’exception
UnsupportedOperationException à chaque fois qu’une méthode souhaite modifier le contenu de la liste.
Voici un petit exemple de manipulations que vous pouvez essayer :
final Vector<String> v = new Vector<String>();
v.add("premier");v.add("deuxieme");
Vector<String> autre = new Vector<String>();
autre.add("troisieme");
v = autre; // KO à la compilation
List<String> u = Collections.unmodifiableList(v);
u.add("quatrieme"); // KO à l’exécution
u.remove(0); // KO à l’exécution
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
11
Compléments sur des concepts et des idiomes Java
'
$
5 Blocs de code « static »
■ Cf. http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
# 11
■ Exécution de code lors du chargement d’une classe
static {
...
}
■ Initialisation des collections immuables sans appel de méthode explicite
■ Idiome utilisé dans la classe ListOfAlgorithm pour initialiser la liste des
algorithmes
■ Idiome utilisé aussi dans la classe AlgorithmElection pour initialiser la collection
d’actions
&
%
Pour plus de sécurité encore, nous souhaitons initialiser la liste des algorithmes et les collections d’actions
des algorithmes de manière automatique sans que le développeur ait besoin d’appeler une méthode au début
de la méthode main. Ainsi, le programmeur ne risque pas d’oublier cette initialisation et c’est l’initialisation
que nous avons prévue qui est exécutée.
Pour ce faire, nous programmons les créations et les initialisations des collections correspondantes dans des
blocs de code dits « static ». Ces blocs sont déclarés de la manière suivante dans les classes ListOfAlgorithms
et AlgorithmElection :
static {
...
}
La machine virtuelle Java charge les classes à la demande lors de l’exécution. Les attributs de classe sont
initialisés lors du chargement et les blocs « static » sont exécutés à la suite. C’est pourquoi ces blocs n’ont pas
besoin d’être nommés car ils ne sont pas appelés par le développeur mais exécutés de manière automatique
par la machine virtuelle Java lors du chargement des classes.
Voici un petit exemple de manipulations que vous pouvez essayer :
public class A {
private int a;
private static int b;
static {
a = 2; // KO à la compilation car a est un attribut d’instance
b = 2;
}
public A(final int a) {
this.a = a;
}
public String toString() {
return "a = " + a + " et b = " + b;
}
}
puis, dans un main:
A a = new A(1);
System.out.println(a);
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
12
Compléments sur des concepts et des idiomes Java
'
$
6 Type énuméré et constructeur de type énuméré
■ Tutoriel Java : http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
■ Livre “Java Efficace” : règle 21
# 12
■ Énumération = classe avec un ensemble d’instances défini à la compilation
■ Chaque instance possède une méthode ordinal
♦ Entier référençant de manière automatique l’instance dans l’ensemble
■ Classe =⇒ Attributs, constructeurs, et méthodes possibles
■ Utilisé dans ListOfAlgorithms pour collecter les collections d’actions des
algorithmes
■ Utilisé dans AlgorithmElection pour indiquer le type du message correspondant à
l’action et pour affecter les numéros des actions
&
%
Une énumération est une classe qui définit à la compilation des (instances) constantes. Comme ces
constantes sont des objets, il est donc possible de les mettre dans des collections. Ainsi, ListOfAlgorithms
définit les constantes représentant les algorithmes et chaque algorithme comme AlgorithmElection définit
les constantes représentant les actions, une action par type de message de l’algorithme.
Les énumérations sont des classes. Par conséquent, il est possible de définir un constructeur permettant
de construire chaque instance en initialisant des attributs. Nous utilisons cette facilité pour indiquer dans
AlgorithmElection le type du message correspondant à l’action. Par ailleurs, chaque instance possède
de manière automatique la méthode ordinal qui retourne un entier qui est la position de l’instance dans
l’ensemble des instances de l’énumération. Nous utilisons ces deux facilités dans les énumérations déclarant
les actions des algorithmes comme AlgorithmElection pour calculer l’indice des actions à partir d’une
constante (indice de la première action de l’algorithme) et de ordinal.
Voici un petit exemple de manipulations que vous pouvez essayer :
public enum E {
E1(200),E2(127),E3(61);
private int a;
public E() {} // KO à la compilation : constructeur public interdit
private E(int a) {
this.a = a;
}
public String toString() {
return "name = " + name() + ", ordinal = " + ordinal();
}
}
puis, dans un main:
for (E e : E.values()) {
System.out.println(e);
}
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
13
Compléments sur des concepts et des idiomes Java
'
$
7 Classes anomymes
■ Cf. http://www.java2s.com/Tutorial/Java/0100__Class-Definition/0201__Anonymous-inner-class.htm
# 13
■ Classes définies comme des classes enfants et n’ayant pas de nom
■ Exemple : dans une instruction, à la suite de l’opérateur new
class A {
some attributes + some methods
}
A b = new A(args) {
other attributes + other methods
};
■ Idiome utilisé dans les définitions des instances de l’énumération
AlgorithmElection
&
%
Dans sa forme simple, la création d’une instance avec l’opérateur new est effectuée comme ceci :
A a = new A(args );
Grâce au polymorphisme, si la classe B est une classe enfant de la classe A, il est possible d’écrire l’instruction suivante :
A b = new B(args );
Une forme plus évoluée permet, à la suite de l’instanciation avec l’appel à l’opérateur new, de définir une
classe enfant qui est aussitôt instanciée. Cette classe enfant est dite « anonyme » (en anglais, anonymous
inner class) car elle n’est pas repérée par un nom dans le code écrit. Dans l’exemple qui suit, les codes des
deux colonnes sont équivalents, la colonne de droite utilisant le mécanisme de classe anonyme définie dans
la même instruction que l’instanciation.
class A {
some attributes + some methods
}
class B extends A {
other attributes + other methods
}
A b = new B(args );
class A {
some attributes + some methods
}
A b = new A(args ) {
other attributes + other methods
};
Les classes anonymes sont souvent utilisées dans les cas suivants :
• définition de classes enfants instanciées une seule fois : cela permet de ne pas multiplier les fichiers de
classes *.java. Cet idiome est très utilisé dans les parties de code de gestion d’interfaces graphiques
ou encore dans les parties de code mettant en œuvre des threads ;
• définition de classes enfants dans les énumérations : c’est l’utilisation que nous en faisons dans
l’application tchat. Par exemple, les méthodes execute des instances des énumérations comme
AlgorithmElection sont définies à la suite de la déclaration de l’instance dans l’énumération.
Cependant, les classes anonymes possèdent des limitations parmi lesquelles les suivantes :
• il n’est pas possible d’instancier une classe anonyme hors de l’instruction qui la définit ;
• l’opérateur instanceof n’est utilisable que sur le type de la classe parent, pas sur le type le plus
spécifique, qui ne possède pas de nom ;
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
14
Compléments sur des concepts et des idiomes Java
• la définition de la classe anonyme ne peut ni inclure de déclaration implements, ni de déclaration
extends.
Voici un petit exemple de manipulations que vous pouvez essayer :
en reprenant la classe A présentée ci-avant :
A b = new A(2) {
private int c;
public String toString() {
return super.toString() + ", c = " + c;
}
};
System.out.println(b instanceof A);
System.out.println(b);
A c = new A(3) implements Serializable {}; // KO à la compilation
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
15
Compléments sur des concepts et des idiomes Java
À l’issu de cette diapositive, nous vous invitons à lire le code des classes de la machine à états. Par
exemple
• pour le serveur, la méthode loop de la classe ChatSelectorMultiServerMain appelle la méthode de
classe execute de la classe ListOfAlgorithms ;
• la machine à états du serveur est initialisée par le bloc de code « static » de la classe ListOfAlgorithms,
qui utilise les attributs des instances construites par le constructeur de l’énumération qui prend en
argument une collection définie en attribut de classe, qui est elle-même construite dans les blocs de
code « static » des énumérations déclarant les actions des algorithmes (par exemple l’énumération
AlgorithmElection) ;
• ainsi, la méthode execute de la classe ChatSelectorMultiServerMain parcourt les algorithmes pour
trouver l’action correspondant au type de message (qui est un entier) et appelle la méthode execute
de l’action trouvée ;
• comme suggéré par l’exemple existant dans la classe AlgorithmElection, ces méthodes d’instance
execute sont définies comme des délégations vers des méthodes de classes regroupées dans une autre
classe : voir par exemple la classe AlgorithmElectionAction ;
• l’effet escompté est de limiter le nombre de lignes de code dans l’énumération AlgorithmElection afin
de voir cette énumération comme la déclaration d’actions, le code des actions étant rassemblé dans une
autre classe.
Télécom SudParis — D. Conan — Juin 2013 — CSC 4509 — ASR4
16