Notes de cours INFO803, M1-IDESSE et M1-MATH-IM

publicité
Notes de cours INFO803, M1-IDESSE et
M1-MATH-IM
Conception et Programmation orientée objet en
Python
Jacques-Olivier Lachaud
LAMA, Université de Savoie
version du 9 février 2010
Table des matières
1 Introduction à l’approche objet
1
1.1
Vision fonctionnelle et vision objet . . . . . . . . . . . . . . . . .
1
1.2
La pertinence de l’approche objet pour l’abstraction . . . . . . .
2
1.3
De l’analyse à l’intégration
. . . . . . . . . . . . . . . . . . . . .
5
1.4
Langage adaptée à l’analyse/conception : UML . . . . . . . . . .
6
1.5
Langage de programmation objet : Python
6
. . . . . . . . . . . .
2 Généralités sur le paradigme objet
7
2.1
Objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
2.2
Vues : conceptuelle, spécification, implémentation . . . . . . . . .
8
2.3
Messages
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2.4
Classes ; instance . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2.5
Attributs d’une classe . . . . . . . . . . . . . . . . . . . . . . . .
9
2.6
Méthodes ; Opérations . . . . . . . . . . . . . . . . . . . . . . . .
9
2.7
Relations entre objets et entre classes . . . . . . . . . . . . . . .
10
2.8
Liens entre objets . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
2.9
Associations entre classes . . . . . . . . . . . . . . . . . . . . . .
11
2.10 Multiplicités d’une association . . . . . . . . . . . . . . . . . . . .
12
2.11 Généralisation, polymorphisme . . . . . . . . . . . . . . . . . . .
13
1
3 Concepts avancés sur les associations
17
3.1
Agrégation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
3.2
Composition
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
3.3
Association qualifiée . . . . . . . . . . . . . . . . . . . . . . . . .
20
3.4
Classe association . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
3.5
Contraintes sur les associations . . . . . . . . . . . . . . . . . . .
21
4 Introduction au langage Python
22
4.1
Collections en Python . . . . . . . . . . . . . . . . . . . . . . . .
22
4.2
TODO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
5 Introduction au langage JAVA
29
5.1
Compilation et exécution d’un programme JAVA . . . . . . . . .
30
5.2
Découpage d’un programme JAVA . . . . . . . . . . . . . . . . .
30
5.3
Types primitifs . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
5.4
Autres types . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
5.5
Tableau d’éléments du même type primitif . . . . . . . . . . . . .
32
5.6
Regroupements à l’aide d’une Classe . . . . . . . . . . . . . . . .
32
5.7
Objets et variables objet . . . . . . . . . . . . . . . . . . . . . . .
34
5.8
Constructeurs d’une classe . . . . . . . . . . . . . . . . . . . . . .
35
5.9
Associations en JAVA . . . . . . . . . . . . . . . . . . . . . . . .
35
5.10 Interfaces, classes abstraites . . . . . . . . . . . . . . . . . . . . .
37
5.11 Généralisation, spécialisation, héritage . . . . . . . . . . . . . . .
38
5.12 Polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . .
38
6 Aperçu de la bibliothèque JAVA
2
38
Introduction
Ce document trace les grandes lignes d’un cours de niveau intermédiaire portant sur la conception par objets et la programmation orientée objet. Le langage
objet choisi est le Python. Le langage de modélisation objet est UML. Ce cours
s’adresse à des étudiants ayant des compétences en programmation impérative,
mais débutant en programmation objet. L’objectif est de connaı̂tre les principes
généraux de l’approche objet (classes, héritage, associations, polymorphisme) et
de savoir les mettre en œuvre.
Pour la notation UML, on se référera au site uml.free.fr. Il existe un
nombre impressionnant de livres sur la programmation objet en Python. On
pourra ainsi regarder le site de (www.python.org) qui contient des tutoriels
Python intéressants.
La lecture de ce document n’est pas forcément linéaire. Notamment, il peut
être utile de lire en parallèle l’introduction au langage Python.
1
Introduction à l’approche objet
1.1
Vision fonctionnelle et vision objet
Dans la vision fonctionnelle, on sépare les données des opérations que l’on
effectue dessus. Le processus de conception naturel est donc d’identifier les
données manipulées, prévoir des structures de données adaptées, puis écrire
des fonctions qui vont prendre une partie de ces données en entrées et fabriquer
des nouvelles données en sortie. On parle d’approche systémique. Cette vision
est bien adaptée à certaines situations. Les bases de données classiques et la
méthode MERISE fonctionnent ainsi sur ce principe. Cela peut être aussi adapté
à certains problèmes de calcul scientifiques, où les données en entrée et en sortie
sont clairement identifiés. En revanche, cette approche devient délicate lorsqu’il
s’agit de modéliser et/ou de simuler des processus plus complexes, avec des
données d’origine variée, qui ne se présentent pas toujours sous la forme. L’approche systémique est alors handicapée, car elle cherche à anticiper précisément
les données qu’elle manipule. On voit aussi que cette vision implique souvent
une analyse ascendante de la problématique, où le concepteur/programmmeur
se pose d’abord la question de comment il va stocker ses données.
Dans la vision objet, on regroupe les données et les opérations qui sont
propres à ces données. Pour résoudre un problème donné, on fait collaborer entre
eux des objets qui vont communiquer entre eux pour résoudre le problème. On
parle de message passing. De fait, on s’intéresse plus aux services/messages que
rendent/comprennent chaque objet qu’à comment les données sont représentées
au sein de l’objet. On dispose de plus de moyens pour regrouper des objets entre
eux, de façon durable ou non.
Normalement chaque objet est responsable de ses données, et un objet ex1
terne n’a pas à les toucher directement. On parle d’encapsulation. Cela fournit
une sécurité supplémentaire et permet de décomposer un problème complexe
en sous-problèmes indépendants. Une fois qu’un objet est bien validé, son fonctionnement est donc garanti dans l’ensemble du système, aucun autre endroit
du programme ne pouvant modifier son comportement.
L’approche objet a été proposée pour pouvoir développer des programmes à
plus grandes échelles, programmes que l’on pouvait ensuite décomposer plus facilement en sous-problèmes. L’approche objet, qui ne considère que les opérations
que peut faire un objet, permet aussi de voir de la même manière des objets
avec des spécificités distinctes, mais quelques services communs. Une autre caractéristique de l’approche objet est de pouvoir définir et manipuler des abstractions, ce qui facilite notamment la conception des programmes. Le polymorphisme permet ensuite d’utiliser des réalisations concrètes et spécifiques des
abstractions.
En informatique, le polymorphisme est l’idée d’autoriser le même code à être
utilisé avec différents types, ce qui permet des implémentations plus abstraites
et générales.
Exemples : L’approche objet est particulièrement efficace pour faire des IHM. On
voit bien qu’une IHM doit faire coexister des composants bien différents (fenêtres,
dessins, boutons, champs de texte ou de saisie, zones de dessins, tableaux) tout
en assurant une mise en page cohérente. Mieux, les dernières IHM permettent
d’intégrer à la volée des composants nouveaux (a priori inconnus) mais qui savent
répondre à quelques messages. Par exemple, Acrobat Reader peut s’ouvrir dans
une fenêtre firefox.
Les scènes 3D sont des arbres d’objets.
On peut modéliser une fonction à l’aide d’un objet. On peut par exemple modéliser
un polynôme sur n’importe quel corps très facilement en programmation objet.
1.2
La pertinence de l’approche objet pour l’abstraction
Quelques exemples pour montrer le processus de résolution d’un problème
dans une approche objet par rapport à une approche impérative.
2
Quelques questions :
1. Comment modéliser une forme dans une approche impérative ?
La question est difficile, car trop floue. On ne voit pas quelle structure de
données va convenir à toutes les formes possibles.
2. Comment modéliser une forme dans une approche objet ?
C’est plus facile, car on se pose simplement la question : que sait faire
une forme ? quelles demandes on va lui faire ? En d’autres termes, à quels
messages l’objet sait-il répondre ?
Par exemple, une forme doit pouvoir donner son aire, son périmètre, son
centre de gravité, ses moments, est-ce qu’un point est à l’intérieur de la
forme ou sur le bord ou à l’extérieur, etc.
3. Dans l’approche objet, ce n’est qu’après qu’on se pose la question de comment écrire ces fonctionnalités. D’ailleurs, l’approche objet permet de facilement découper le travail en spécialisant les formes.
On aura donc un type Forme, et plein de sous-types qui réalisent des formes
particulières : le carré, le disque, le rectangle, l’ellipse, le polygone, la courbe
radiale, etc.
4. On aura donc des types abstraits (e.g. Forme) et des types concrets (e.g.
Carre), et des relations de sous-typage. Tout type Python indique les messages auxquels il sait répondre : ce sont les fonctions ou méthodes associées
au type.
5. On peut se poser la même question pour modéliser un animal, une fonction
mathématique, une molécule chimique, une variable statistique, etc.
3
Quelques questions :
1. On dispose dans un carré de côté 1, un ensemble de N carrés de côtés
h < 1 deux à deux disjoints. Problème : quelle est la probabilité pour
qu’un clic à des coordonnées aléatoires soit dans une des formes ?
2. Si maintenant les carrés n’ont pas tous le même côté. Solution algorithmique ?
3. Si maintenant on a des carrés ou des rectangles ?
4. Si maintenant on peut avoir aussi des cercles ou des ellipses ? On se donne
une fonction aire spécialisée pour chaque cas et l’approche objet est alors
naturelle.
class Forme:
def Aire():
pass
class Carre( Forme ):
def __init( self, cote ):
self._cote = cote
def Aire():
return self._cote * self._cote
class Cercle( Forme ):
def __init( self, rayon ):
self._rayon = rayon
def Aire():
return math.pi * self._rayon * self._rayon
Quelques questions :
1. Comment modéliser un vaisseau spatial dans une approche impérative ?
Difficile. Sera en plus spécifique à chaque vaisseau.
2. Comment modéliser un vaisseau spatial dans une approche objet ?
On peut commencer par préciser les objets qui vont interagir : capitaine,
navigateur, chef des transmissions, pilote, mécanicien, etc.
Puis on peut préciser les messages auxquels ils savent répondre et les objets
avec lesquels ils peuvent interagir.
Le capitaine connait tout l’équipage et sait communiquer avec eux. Le navigateur sait calculer une trajectoire pour un objectif donné avec l’ordinateur
de bord. Le pilote sait diriger le vaisseau vers un objectif. Le mécanicien
sait surveiller et réparer le moteur...
Représenter tous ces objets ainsi qu’une séquence de communication suivi
par une décision du capitaine d’aller vers une planète.
4
Quelques questions :
1. Comment modéliser un point dans le plan dans une approche impérative ?
Même question pour un nombre complexe ? Que constatez-vous ?
Ce sont les mêmes données. On pourrait choisir le même type.
2. Comment modéliser un point dans le plan dans une approche objet ? Même
question pour un vecteur à deux composantes ?
Si on raisonne en termes d’opérations (ie de messages), ce ne sont pas les
mêmes objets, donc on créera deux classes différentes.
1.3
De l’analyse à l’intégration
Dans tout processus d’informatisation, il est important de distinguer plusieurs étapes (qui ne sont pas forcément réalisées les unes après les autres).
Nous en mettons en valeur quelques unes :
Analyse (Fait par l’analyste). Il doit comprendre le besoin du client et le traduire sous forme de spécifications. Parfois il est aussi responsable du cahier
des charges, qui est un document précisant l’attente du client, et qui est le
document contractuel. Il doit comprendre le “Pourquoi” ou le “quoi” du
projet. On parle souvent d’analyse fonctionnelle, car il identifie les fonctionnalités attendues. Souvent, il est chargé du chiffrage, en collaboration
avec le concepteur.
Conception (Fait par le concepteur, ou architecte/concepteur dans des gros
projets). Son rôle est de préciser comment réaliser une solution informatique au problème posé, à partir des spécifications de l’analyste. Il
doit par exemple proposer un ensemble de systèmes, programmes, soussystèmes, sous-programmes et leur organisation, de manière à montrer un
enchaı̂nement global qui répondra au problème posé. Il doit aussi penser à
la maintenance future de son programme, et l’anticiper dans sa conception.
Chargé du “Comment”.
Développement/programmation (Fait par le développeur/programmeur) Il
traduit les directives de conception dans un langage/système donné, avec
ses spécificités propres. Il doit veiller à respecter à la fois la structure mais
aussi les comportements et sémantiques des spécifications. Il sait aussi
réutiliser des codes existants. Chargé de la “Fabrication”.
Test (Fait par le testeur) Il est chargé de valider le programme développé,
notamment en vérifiant que les spécifications initiales du cahier des charges
sont respectées.
Déploiement/intégration (Fait par l’intégrateur) Il installe l’application développée dans son contexte client et vérifie son intégration. Eventuellement, il paramètre le logiciel en fonction de l’environnement.
Remarque : la plupart des études sur le cycle de vie d’un logiciel montre
que plus de 70% des coûts proviennent des deux dernières phases. Il est donc
important de bien les anticiper dans les phases préparatoires.
5
1.4
Langage adaptée à l’analyse/conception : UML
[Wikipédia] UML (en anglais Unified Modeling Language, « langage de modélisation unifié ») est un langage graphique de modélisation des données et
des traitements. C’est une formalisation très aboutie et non-propriétaire de la
modélisation objet utilisée en génie logiciel. L’OMG travaille actuellement sur la
version UML 2.1. Il est l’accomplissement de la fusion des précédents langages
de modélisation objet Booch, OMT, OOSE. Principalement issu des travaux de
Grady Booch, James Rumbaugh et Ivar Jacobson. UML est un standard défini
par l’OMG (Object Management Group).
Il ne s’agit que d’une notation, mais UML fournit un langage commun assez
clair pour modéliser efficacement l’analyse et la conception objet.
1.5
Langage de programmation objet : Python
[Wikipédia] Python est un langage de programmation interprété multi-paradigme.
Il favorise la programmation impérative structurée, et orientée objet. Il est
doté d’un typage dynamique fort, d’une gestion automatique de la mémoire par
ramasse-miettes et d’un système de gestion d’exceptions ; il est ainsi similaire à
Perl, Ruby, Scheme, Smalltalk et Tcl.
Python est un langage d’assez haut niveau, contrairement à C ou C++.
Il fournit en interne des structures de données très puissantes et très faciles
à composer. Comme il n’impose que très peu de déclarations, il autorise du
développement très rapide. Son côté interprété le rend très pratique comme
langage script cross-plateforme. Il est donc très utilisé pour les applications webs.
Néanmoins, on constate généralement une perte de performance (de l’ordre de
10 à 20 contre 1 par rapport au C selon des benchmarks).
Voilà ci-dessous un premier exemple de code Python (fichier first.py).
# Premier code Python
print( ’Hello world !’ )
On peut soit choisir d’utiliser un environnement de développement intégré
(par exemple idle), soit choisir son éditeur de textes et utiliser les commandes
en-ligne :
Exécution. On utilise la commande python.
# Sous un shell, exécute le fichier first.py
python first.py
’Hello world !
6
2
Généralités sur le paradigme objet
2.1
Objets
UML est une notation et définit des éléments de modélisation (notamment
graphiques), leurs relations et leurs utilisations pour représenter symboliquement une abstraction d’un problème donné et de la solution de modélisation
retenue.
Objet : entité/unité ayant une identité et une frontière bien délimitée, possédant
des données (les attributs) et offrant des services (les méthodes ou opérations).
La notion d’identité est fondamentale : 2 objets sont toujours distincts.
Dans l’approche objet, un objet peut représenter une entité réelle (e.g. un
livre dans une BD d’un SI bibliothèque, un appartement dans un SI de gestion
immobilière, un salarié dans une entreprise, une facture), une entité conceptuelle
(e.g. une date, une transaction bancaire, un calcul ou une équation), une entité
informatique (une structure de données, un élément d’interface graphique).
Exemple : Henri Martin, client de la banque SG a une carte bancaire 1234.5678.
Il va retirer de l’argent au GAB SG du cours Gambetta. Celui-ci vérifie avec le
SI de la SG que la carte correspond à un compte de la SG. Dessinez les objets
participant à ce cas d’utilisation.
Henri Martin : ClientCB
carte 1234.5678
: GAB SG
cours Gambetta
: SI SG
Comptes
Quelques remarques :
– l’état d’un objet est la valeur de ses attributs.
– Deux objets dans le même état sont appelés des clones. En aucun cas, il
ne désigne le même objet. Il y a juste coı̈ncidence de valeurs.
– Un objet est toujours une entité concrète (et ne peut être la réalisation
d’une classe abstraite, cf. plus loin).
– Un objet a une durée de vie. Il nait au moment de sa création ou instanciation et disparait à sa destruction.
Une classe est un regroupement d’objets ayant des propriétés communes
(mêmes méthodes, mêmes types et dénominations des attributs, mais pas forcément
les mêmes valeurs).
Exemples :
– Les bibliothèques en France fournissent à peu près toutes les mêmes services :
fond documentaire, abonnement, consultation, prêt, emprunt, achats. Chaque
bibliothèque spécifique (exemple : celle de Chambéry) est un objet. On peut
voir le dénominateur commun aux bibliothèques comme une classe, qui offre ces
services, mais dont le fond documentaire, les employés et les usagers varient.
– Identifiez les types de documents dans une bibliothèque. Cherchez à voir ce qui
est commun de ce qui est différent. En déduire des classes.
7
2.2
Vues : conceptuelle, spécification, implémentation
Lorsque l’on fait de la modélisation objet, il faut choisir un niveau de représentation
qui déterminera souvent ce que sont les objets. Il y a ainsi 3 points de vue sur
un système :
– le point de vue conceptuel : modélisation du réel, du domaine, du métier.
Utile au moment de l’analyse.
– le point de vue spécification : niveau logiciel mais seulement interfaces et
relations, sans penser au codage réel. Utile au moment de la conception.
– le point de vue implémentation : niveau logiciel, représente ce qui est effectivement codé (e.g. les classes utilisées pour implémenter les collections
sont données). Utile au niveau codage.
Il ne faut pas mélanger les vues, et les faire dans l’ordre (sauf pour du reverseengineering).
L’intérêt de l’approche objet est de montrer que ces niveaux de représentation
différents fonctionnent de la même façon. Il est par exemple extrêmement
fréquent de plus de retrouver les objets identifiés au niveau métier comme des
objets persistents du niveau implantation. En général, on a de plus en plus
d’objets en descendant dans les niveaux.
2.3
Messages
Les objets communiquent entre eux en s’échangeant des messages. C’est le
principe du message passing. Un message se caractérise par un nom ainsi que par
ses pièces jointes ou paramètres : on parle de signature du message. La liste des
messages auxquels peut répondre un objet définit son type. A chaque message
que peut comprendre un objet est associé en général une action. Un message se
symbolise par une flèche surmontée du nom du message. Le message peut être
synchrone ou un simple appel de fonction (il est bloquant), ou asynchrone (non
bloquant).
Quelques questions : Quel est le type d’une chaı̂ne de caractères ? I.e. que
souhaitez-vous pouvoir faire avec une chaı̂ne de caractères.
2.4
Classes ; instance
Classe : abstraction d’objets ayant des propriétés similaires mais des attributs
pouvant varier. En d’autres termes, une classe permet de représenter dans son
ensemble un regroupement d’objets de même type ou de types similaires.
En Python, une classe est définie par le mot-clé class. Il n’est nul besoin
de prévoir les attributs à stocker. C’est juste en faisant des affectations dans
l’objet (self.attr = ...) que l’on crée à la volée des attributs. En revanche,
on peut spécifier des méthodes à l’aide de la commande def. Chaque objet
pourra ensuite avoir des valeurs distinctes pour ses attributs, mais leurs noms,
nombres et types sont fixés. Python suit donc donc une définition très générale
8
d’“objet”, où c’est à la demande (à l’exécution) que l’on vérifiera si l’objet a tel
attribut ou telle méthode. Ainsi, on peut par exemple ajouter des méthodes à
un objet au cours de sa durée de vie.
Une instance de classe est une réalisation concrète d’une classe ; Un objet est
donc une instance de sa classe qui le représente.
Exemple : Code d’une classe Point. Puis p1 = Point( 5, 0 ) ; p2 = Point(
10, 4 ) ;. p1 et p2 sont des variables qui pointent vers des instances de classe
Point. Ils ont les mêmes propriétés (i.e. ont les mêmes méthodes), des attributs
de même type mais de valeurs différentes.
En Python, pour définir des objets, il faut nécessairement définir une classe
qui correspond.
2.5
Attributs d’une classe
Les attributs sont caractéristiques de toutes les instances de la classe, même
s’ils peuvent prendre des valeurs différentes. Attribut et association sont deux
notions proches.
– Nommage et typage des attributs
– Modifications de visibilité : public (+), protégé (#), privé (-) (défaut :
privé)
– état d’un objet
non Utilisation de notes pour contraindre les attributs
non Notion d’attribut dérivé (déjà vu en Merise), noté “/”
Exemple : Spécification de Point. Spécification de Complexe. Identiques ? Oui au
niveau des attributs.
Exemple : Exemple d’attribut dérivé :
Personne
{ age = date actuelle − date de naissance }
2.6
Nom
Date de naissance
/ Age
Méthodes ; Opérations
Les méthodes ou opérations sont les messages auxquels savent répondre tous
les objets de cette classe, en d’autres termes les processus que tout objet de
la classe sait mener à bien. Pour raccourcir, on dira souvent les méthodes ou
messages que la classe sait mener à bien.
– Représentation des opérations : nom, paramètres, typage du retour
– Modifications de visibilité : public, protégé, privé (défaut : public)
9
Exemple : Un Point peut être construit à partir de deux coordonnées. Il sait
calculer sa distance à un autre point.
Point
- x : réel
- y : réel
+Point( x0 : réel, y0 : réel)
+distance(p : Point) : réel
Spécification des méthodes de Complexe. Identique à Point ? Non au niveau
des opérations. On modélise donc les points et les complexes dans des classes
différentes. On verra une autre solution avec les liens/associations plus tard.
Quelques questions : Modélisez un polygone.
Remarque : ce n’est pas parce qu’un attribut ou une opération ne sont pas
représentés qu’ils n’existent pas ! Le diagramme n’est qu’une vue, éventuellement
partielle, de la classe.
Voilà un exemple Python montrant comment instancier des objets dont les
classes sont déjà définis dans les bibliothèques d’IHM.
from Tkinter import *
# Cree une fen^
etre
fen1 = Tk()
# lui ajoute un texte rouge
tex1 = Label( fen1, text=’Bonjour tout le monde !’, fg=’red’)
tex1.pack()
# et un bouton qui, lorsqu’on clique dessus, détruit la fen^
etre.
b = Button( fen1, text=’Export PS’, command = fen1.destroy )
b.pack()
# lance la boucle d’attente des événements.
fen1.mainloop()
2.7
Relations entre objets et entre classes
Les relations indiquent des dépendances statiques entre différentes classes.
Ce sont souvent :
– association matérialisant un lien entre objets.
– relation de généralisation/spécification ou sous-type
Dans un premier temps, nous présentons les associations, dont les liens en
sont les instances. La relation de généralisation sera abordée ensuite.
2.8
Liens entre objets
Certains objets ont souvent des relations particulières avec d’autres objets,
relations dont l’existence est supérieure à l’exécution d’une simple opération et
persiste dans une certaine durée. On parle de lien entre objets. La notion de
lien est très proche de la notion de relation en mathématiques.
10
Un tel lien permet à un des objets reliés d’envoyer des messages à son partenaire et ainsi de lui demander des services. Par ce biais, plusieurs objets vont
collaborer à la réalisation d’une tâche. Un lien se dessine comme un trait entre
deux objets.
Exemple : L’entreprise 1 a un seul fournisseur le fournisseur 1, sans avoir de
voiture d’entreprise. L’entreprise 2 a deux fournisseurs (le 1 et le 2) et deux
voitures d’entreprise (1 et 2).
Fournisseur 1
Entreprise 1
Fournisseur 2
Entreprise 2
Véhicule 1
Véhicule 2
Un tel diagramme est dit diagramme d’objets. On peut nommer les liens ou
définir des rôles pour chaque intervenant du lien.
Quelques questions : Italine et Pascal sont les parents de Zoë et Clément. Pascal
a pour parents Jean et Constance. Les parents d’Italine sont Robert et Martha.
Ils ont eu deux autres enfants Max et Gwen. Jean a eu une autre fille Eloı̈se avec
Sibylline. Eloı̈se a eu deux enfants Lucien et Raymond avec Honoré. Gwen a un
enfant dénommé Bob. Matérialisez les liens parents/enfants. Matérialisez ensuite
les liens de cousinage.
Est-ce intéressant d’indiquer si père ou mère dans la parentée ? La notion
de marriage est-elle intéressante à rajouter ? Pourquoi le cousinage/ancètres
n’est pas en général stocké en mémoire ?
2.9
Associations entre classes
En général : une association est une connexion bidirectionnelle entre deux
classes indiquant un rapport ou une interaction entre elles. Il s’agit d’une vision
statique permettant de caractériser les liens possibles entre instances de ces
classes. Les associations résument les liens possibles des instances.
Le diagramme représentant les classes avec leurs associations/relations s’appelle diagramme de classes.
11
Exemple : Voilà une première solution pour le diagramme de classe des entreprises/fournisseurs.
Entreprise
Fournisseur
Véhicule
Expliquer que le rapport classe/objet est le même que le rapport association/lien (voir schémas suivants).
Une relation anonyme est rarement significative ; deux méthodes existent
pour la préciser :
– Il est possible de la nommer en plaçant une forme verbale active ou passive
en son milieu. On peut aussi y placer un triangle plein pour indiquer le
sens de lecture si nécessaire.
– On peut indiquer le rôle de chaque extrémité de l’association du point de
vue de la classe située à l’autre extrémité (à préferrer).
Ces deux décorations d’association sont exclusives.
Contrairement aux liens, les associations peuvent être réflexives.
Quelques questions : Corrigez l’exemple précédent d’entreprise/fournisseur en
montrant qu’un fournisseur est une entreprise et en donnant des rôles à l’association.
Quelques questions : Proposez un diagramme de classes pour la généalogie de
l’Exercice 2.8. 2 solutions à ce moment : une classe Personne (avec un attribut
sexe et une contrainte) ou 2 classes Homme et Femme.
2.10
Multiplicités d’une association
Il est intéressant de placer en regard de chaque extrémité d’une association sa
multiplicité, qui précise combien d’instances de cette classe peuvent participer
à cette relation. Le diagramme d’objets peut servir de base pour définir les
multiplicités dans le diagramme de classes.
La cardinalité d’une multiplicité est de la forme :
n
n..m
*
n..*
Exactement ”n” (entier naturel ¿ 0)
De ”n” à ”m”
Plusieurs (équivalent à ”0..n” et ”0..*”)
”n” ou plus
Insistez sur le fait qu’en UML, multiplicités et rôles sont inversés sur l’association par rapport à MERISE.
12
1..*
Entreprise
1..* Fournisseur
1
0..*
Véhicule
Quelques questions : Mettre les multiplicités sur le diagramme de classes
généalogique. Attention au 1 exactement qui impliquerait une BD infinie !
Quelques questions : Modélisez une liste simplement chaı̂née composée
d’éléments. Même chose en doublement chaı̂née. Expliquez pourquoi il faut un
type pour la liste et un autre pour l’élément encapsulant la donnée.
Quelques questions : Modélisez une carte routière (e.g. classes : route, intersection) dont voilà une solution parmi d’autres.
2.11
2.11.1
Généralisation, polymorphisme
Typage et classification
On rappelle que le type d’un objet est l’ensemble des messages auxquels il
sait répondre. Ce type décrit syntaxiquement et sémantiquement les messages
auxquels l’objet peut répondre. Pour un langage de programmation classique,
c’est l’ensemble de ses méthodes caractérisées par leur signature. En Python,
chaque objet est du type défini à son instanciation en l’occurence sa classe.
Néanmoins, Python autorise le programme à étoffer son objet de nouveaux
attributs en cours de vie de l’objet. En ce sens, Python ressemble aux langages
objets Eiffel ou Smalltalk.
Le paradigme objet permet de composer les types/classes entre eux/elles pour
créer de nouveaux types/classes. Une relation très importante est l’héritage.
En Python, on définit un type (abstrait ou concret) sous forme d’une classe
avec la syntaxe :
class <nom> (object) :
On note que, contrairement à d’autres langages, Python ne distingue pas
vraiment classes abstraites (ou interfaces) et classes concrètes. En effet, Python
utilise le principe du duck typing : si ça cancanne comme un canard et dandine
comme un canard, c’est un canard. En d’autres termes, si l’objet possède la
méthode au moment de son appel, l’interpréteur exécute la méthode, sinon il y
a erreur. Mais on voit bien que le code n’est pas forcément structuré au préalable.
C’est juste à l’exécution que tout est vérifié.
13
# Exemple de sous-typage.
# Type représentant tout félidé. Seuls les services sont spécifiés.
class Félidé( object ):
def crier():
raise NotImplementedError
# Lion est un sous-type de Félidé, car tous les félidés ne sont pas des lions.
# En tant que classe, on met en oeuvre le cri par un rugissement.
class Lion (Félidé):
def crier():
rugir()
...
# Chat est un sous-type de Félidé, car tous les félidés ne sont pas des chats.
# En tant que classe, on met en oeuvre le cri par un miaulement.
class Chat ( Félidé ):
def crier():
miauler()
Fig. 1 – Exemple de sous-typage en Python. On voit qu’il faut rajouter une
erreur pour rendre la classe Félidé non instanciable (donc abstraite).
2.11.2
Héritage, classes et classification
Rappeler la définition ensembliste des types. Une classe est un moyen de
réaliser la classification d’entités/objets du modèle. A priori, les objets d’une
même classe ont des attributs et propriétés similaires.
Exemple : Le zoo a un ensemble d’animaux. Mettre dans une même patate tigres,
lions, crocodiles, etc. Ensuite mettre dans une super-patate tigres et lions et la
nommer félidés. La généralisation est donc une simple inclusion. On peut faire
de la spécialisation en différenciant mâle et femelle pour chaque animal. On peut
ensuite faire des patates mâles et femelles. On voit ainsi la notion d’héritage
multiple.
L’héritage est la faculté d’une sous-classe ou d’un sous-type d’hériter des
propriétés de son parent et de les affiner. Dans cette démarche, il existe au
moins deux mécanismes d’héritage :
– le sous-typage ou l’héritage d’interface (Figure 1), et
– la sous-classification ou l’héritage de mise en oeuvre (Figure 2).
Le sous-typage est donc le processus par lequel on restreint l’espace des valeurs du type parent, et la sous-classification est le processus par lequel on
récupère et on spécialise la mise en oeuvre (redéfinition).
Python ne distingue pas vraiment ces deux notions, car il n’a pas vraiment
la notion d’interface.
Une classe peut hériter de plusieurs classes. On le sépare par des virgules
entre les parenthèses.
14
# Exemple de sous-classification.
# Classe représentant un rectangle du plan.
class Rectangle( object )
def __init__( self, x, y, l, h ):
self._x = x
self._y = y
self._w = w
self._h = h
def aire():
return self._l * self._h
# Classe représentant un carré du plan, obtenu par sous-classification de
# Rectangle.
class Carre( Rectangle ):
def __init__( self, x, y, c ):
Rectangle.__init__( self, x, y, c, c )
# redéfinition de aire pour spécialiser sa mise en oeuvre.
def aire():
return self._l * self._l
Fig. 2 – Exemple de sous-classification, ou héritage de classe/mise en oeuvre en
Python.
2.11.3
Polymorphisme, redéfinition
Le polymorphisme est l’idée d’autoriser le même code à être utilisé avec
différents types, ce qui permet des implémentations plus abstraites et générales.
Pour le mettre en œuvre, une classe définie comme sous-classification d’un classe
C ou sous-type d’un type T , peut (re)définir la mise en œuvre des méthodes
spécifiées dans C ou dans T . Le principe est de réécrire la signature de la
méthode et son corps : c’est la redéfinition.
Plus précisément, une portion de code attendra de manipuler un objet A
d’un type donné T pour lui envoyer le message m(. . .). A l’exécution, n’importe
quel objet B dont le type est un sous-type de T pourra se substituer dans cette
portion de code. De plus, B conservera la spécificité de sa mise en œuvre de
la méthode m(. . .). En fait, à l’exécution, le programme détermine la classe
effective de l’objet A pour appeler sa redéfinition de méthode.
En Python, c’est très simple. Toute méthode est forcément polymorphe. Un
objet d’un type donné possède une liste de méthodes connues. Si on appelle une
de ses méthodes elle est appelée.
L’exemple ci-dessous utilise les classes définies dans la Figure 2.
15
...
r = Rectangle( 0, 0, 10, 5 )
c = Carre( 0, 0, 10 )
print( r.aire() )
# appelle Rectangle.aire()
print( c.aire() )
# appelle Carre.aire()
t = [ r, c ]
# Aucun problème : Python manipule des collections
# arbitraires.
aireTotale = 0.0;
for f in t
aireTotale = aireTotale + f.aire();
# appelle Rectangle::aire() sur t[ O ]
# appelle Carre::aire() sur t[ 1 ] : Polymorphisme
Le concept de polymorphisme joint à celui d’héritage fait toute la puissance de
l’approche objet. Les objets sont manipulables de façon générique, mais peuvent
être spécialisés partout où c’est utile. C’est pourquoi on parle souvent de composants en programmation objet.
En Python, le duck typing simplifie tout cela, car il ne demande a priori
aucune structuration entre classes pour faire du polymorphisme. A l’exécution,
on vérifie juste que la méthode existe dans l’objet concerné.
2.11.4
Héritage en UML
En UML, on symbolise la relation d’héritage par une flèche terminée par un
triangle vide. On parle aussi de relation de généralisation (dans un sens) ou de
spécialisation (dans l’autre sens).
Etre vivant
Végétal
Animal
Vertébré
Arthropode
Généralisation simple. Elle exprime que les éléments d’une classe sont aussi
décrits par une autre classe : c’est la relation “est une sorte de”. C’est une
manière de classifier les classes en une hiérarchie où chaque classe a au
plus une superclasse.
Généralisation multiple Les classes peuvent avoir plusieurs superclasses. La
généralisation multiple consiste à fusionner plusieurs classes en une seule.
Cela permet d’exprimer qu’une classe est plusieurs choses en même temps,
et rassemble donc plusieurs ensembles de propriétés.
On note que l’héritage d’interface se note aussi avec la relation △, avec un
tracé de la relation par des tirets.
16
Quelques questions : Les véhicules peuvent être aériens ou terrestres. Un tapis
volant est un tapis qui est aussi à l’aise pour transporter des gens dans l’air que
sur terre. Dessinez le diagramme de classes.
3
Concepts avancés sur les associations
Cette partie présente différents concepts liés aux associations : spécialisation
d’association, restrictions, contraintes.
3.1
Agrégation
3.1.1
Définition et notation
Une agrégation représente une association non symétrique dans laquelle une
des extrémités joue un rôle prédominant par rapport à l’autre. Quelle que soit
l’arité, elle ne concerne qu’un seul rôle d’une association.
L’agrégation permet par exemple de définir qu’un objet est composé d’un
ou plusieurs sous-objets. Elle permet une lecture plus rapide des diagrammes
de classe, même si en théorie il suffirait d’avoir des associations. Notons que
l’agrégation n’impose a priori pas de réalisation particulière.
3.1.2
Utilisation
Pour définir une relation d’agrégation entre des objets, il faut respecter les
règles suivantes :
– Informellement, l’agrégation respecte les règles suivantes
antisymétrie : si un objet A fait partie d’un objet B, alors un objet B
ne fait pas partie d’un objet A, même indirectement. Si tel n’est pas
le cas, cela veut dire qu’on est en présence d’une simple association.
17
transitivité : si un objet A fait partie d’un objet B, et que B fait partie
d’un objet C, alors A doit faire partie d’un objet C dans tous les cas.
Sinon, c’est que les champs sémantiques des objets sont mal définis.
– Plus formellement, écrire une agrégation entre deux classes (distinctes ou
non) impose que toute instanciation de ces classes et associations sous
forme de graphe des liens orientés de l’agrégat vers l’agrégé ne contient
pas de cycle.
L’agrégation peut être utilisée dans les cas suivants :
1. pour les objets qui sont les parties d’un assemblage. Le tout a un comportement global cohérent. Chaque constituant conserve un fonctionnement
propre. Exemple : un clavier fait partie d’un ordinateur.
2. pour les objets qui forment les matériaux définissant ensembles un objet.
Chaque constituant n’existe plus vraiment en tant que tel mais fait partie
d’un tout. Exemple : le pain est fait de farine, d’eau et de levure.
3. pour les objets qui sont des portions d’un objet plus conséquent. Exemple :
une coupe dans une image médicale 3D.
4. pour les objets qui sont en relation spatiale ou géographique d’inclusion
avec un autre objet. Exemple : Yosemite fait partie de la Californie.
5. pour les objets qui collectés et ordonnés définissent un tout cohérent.
Exemple : un voyage est composé de plusieurs trajets.
6. pour les objets contenus dans un conteneur quelconque, sans être forcément
du même champ sémantique. Exemple : les employés comme les ressources
matérielles font partie d’une entreprise.
3.1.3
Multiplicité
Les agrégations peuvent être multiples, même du côté de l’agrégat.
Des personnes peuvent être copropriétaires de plusieurs immeubles.
18
Quelques questions :
– Reprenez le diagramme de généalogie vu précédemment. Y a-t-il lieu de remplacer une simple association par une agrégation ? Dans les deux cas, expliquez
pourquoi ?
– Dans une structure d’arbre ou de liste, doit-on employer des agrégations ou des
associations ?
– Une boı̂te englobante englobe un certain nombre non-nul de points. Une boı̂te
englobante définit aussi 2 points particuliers pour représenter son coin supérieur
gauche et son coin inférieur droit. Un point peut appartenir à plusieurs boı̂tes
englobantes.
– Question supplémentaire : on met en relation les boı̂tes englobantes qui sont
incluses l’une dans l’autre et les boı̂tes ainsi que les boı̂tes avec une intersection
vide. Mettez à jour le diagramme de classe.
3.2
Composition
La composition est un cas particulier de l’agrégation, qui implique une inclusion plus forte de l’objet agrégé dans l’objet agrégeant. Alors qu’un même objet
peut être agrégé par plusieurs objets différents, un même objet ne peut pas être
“composé” (ou partagé) par plusieurs objets.
En UML, on utilise la même notation pour la composition que pour l’agrégation,
sauf que la pointe ♦ est noircie en ¨.
La composition implique une coı̈ncidence des durées de vie des composants et
du composite : la destruction du composite implique la destruction de tous ses
composants. La création, la modification et la destruction des divers composants
sont de la responsabilité du composite. Du côté de l’agrégat, la multiplicité est
0 ou 1 (0 : attribut non renseigné).
La composition et les attributs sont sémantiquement équivalents. La notation
par composition s’emploie dans les diagrammes de classes lorsqu’un attribut
participe à d’autres relations dans le modèle.
19
Exemples : Un B^
atiment se compose d’un certain nombre de Salles.
Batiment
Salle
1..∗ mon numero : entier
¨
⇔
Batiment
mes salles [1..* ] : Salle
La différence entre une composition et un attribut est que la modélisation
d’un attribut nécessite la spécification implicite de son type, et non un sous-type,
alors que la composition accepte le polymorphisme. Les attributs sont donc un
cas particulier de relations de composition : composition par valeur.
A noter que la composition permet aussi de représenter facilement les attributs non renseignés (ou non valués).
Quelques questions :
– Faites le diagrammes de classes des voitures, roues, moteurs. Rajoutez un
mécanicien qui peut changer la roue d’une voiture.
– Reprenez l’exemple des fonctions. Y a-t-il des agrégations/compositions ?
3.3
Association qualifiée
Une association qualifiée est une association qui permet de restreindre les
objets référencés dans une association grâce à une clef. Pour naviguer de la
classe A vers la classe B via l’association qualifiée du côté de A, l’utilisateur en
spécifiant cette clef restreint le nombre d’objets B reliés. Très souvent, seul un
objet B est relié à cet objet A avec cette clef.
Exemples :
– Un train se compose de wagons, identifié par un numéro. Chaque wagon se
compose d’un certains nombre de sièges, identifié aussi par leur numéro.
– Un plateau de jeu d’échec se compose de 64 cases, identifiable par leurs coordonnées (A-H, 1-8).
3.4
Classe association
C’est une classe faisant partie d’une association, et qui permet de stocker des
données comme d’associer un comportement à un lien entre deux objets.
20
Exemples :
– Créer un diagramme de classe permettant de modéliser la relation de mariage
entre deux personnes. Où doit-on stocker la date et le lieu du mariage ?
– Dans D & D, un passage entre deux salles peut-il être modélisé comme une
classe-association ?
On note qu’on peut éclater toute classe-association en deux associations et
une classe.
3.5
Contraintes sur les associations
La multiplicité est déjà une contrainte. Les autres contraintes se représentent
dans des diagrammes par des expressions entre accolades.
– La contrainte {ordonné} peut être placée sur le rôle pour spécifier qu’une
relation d’ordre décrit les objets de la collection. Le modèle ne spécifie
comment les éléments sont ordonnés, mais seulement que l’ordre doit être
maintenu durant ajout et suppression des objets par exemple.
Exemples : Dans une File d’attente, les Personnes sont ordonnées.
– La contrainte {sous-ensemble} indique qu’une collection est incluse dans
une autre collection. La contrainte est placée à proximité d’une relation
de dépendance entre deux associations, la flèche indique le sens de la
contrainte).
Exemples : Les délégués sont tous des parents d’élèves.
Parent
Ecole
{ Sous−ensemble }
Personne
Délégué
– La contrainte {ou-exclusif} indique que, pour un objet donné, une seule
association parmi un groupe d’association est valide. Cela évite l’introduction de sous-classes artificielles pour matérialiser l’exclusivité.
Exemples : La sortie d’un amplificateur peut être connectée à un casque ou à une
enceinte.
Les extrémités d’association peuvent aussi être contraintes : {variable}
(défaut), {gelé} (ie constant), {ajoutUniquement} (pour une multiplicité supérieure
à 1).
Exemples : Un œil a une couleur ({gelé}). Une sculpture est
faı̂te d’un matériau ({gelé}). Un historique est composé de faits
({ajoutUniquement}).
21
Quelques questions : Un polygone est composé de points. Faı̂tes le diagramme
de classes correspondant.
class Point { ... }
class Polygone {
Point[] m_sommets;
// Cree un polygone a nb sommets. Les coordonnees
// sont invalides.
Polygone( int nb ) {
m_sommets = new Point[ nb ];
}
// Donne les coordonnees de p au sommet numero i.
void setSommet( int i, Point p ) {
m_sommets[ i ] = new Point( p.x, p.y );
}
4
Introduction au langage Python
Le Python est un langage interprété, ou langage de script. Un programme
(dénommé python) lit le code source (un fichier texte) ligne à ligne et exécute
les commandes spécifiées. L’avantage est donc qu’un programme écrit en Python
est exécutable sur n’importe quelle machine, du moment que vous avez installé
python dessus). L’inconvénient est qu’un programme Python est en général plus
lent que le même programme écrit dans un langage compilé (comme C, C++,
Pascal, Ada).
Le Python permet la programmation impérative, objet, et fonctionnelle. Il
est donc très versatile. Il est conçu pour rendre plus rapide le développement
d’un programme, en minimisant le travail de spécification du programmeur.
Par exemple, en C, C++ ou JAVA, il faut prévoir tous les types de données
et déclarer leurs attributs et leurs méthodes avant de pouvoir les utiliser. Le
Python n’impose rien de tout cela. L’utilisateur peut rajouter quand il le veut
un nouvel attribut à un objet.
Ce typage est déroutant au début pour qui est habitué à du C++ (très
rigide) et JAVA (assez rigide). Il permet néanmoins un apprentissage rapide de
la programmation objet et de sa principale qualité : le polymorphisme.
4.1
Collections en Python
Python propose plusieurs structures de données pour représenter les collections d’éléments. Chacune est bien adaptée à certaines situations. Conver22
tir l’une en l’autre est facile. Les tuples et les listes sont deux catégories de
séquences.
4.1.1
Les uplets ou tuples
Il s’agit d’une séquence non modifiable d’éléments. On les crée et manipule
ainsi :
# tuple à 0 élément
t0 = ()
# tuple à 1 élément
t1 = ’hello’,
# tuple à plusieurs éléments
t3 = 1, 2, 4
t4 = ( 6, ’toto’, 4, ’yup’ )
# tuple de tuples
tt = ( (3,2), (5,6), (4,6,7) )
print( len( tt ) )
# donne 3
x, y, z = tt
# donne x = (3,2), y = (5,6 ), z = (4,6,7)
# On aurait pu écrire
x = tt[ 0 ]
y = tt[ 1 ]
z = tt[ 2 ]
Les tuples sont représentés efficacement en Python. On a donc intérêt à les
utiliser de préférence à des listes lorsqu’on connait priori le nombre d’éléments
de la séquence et que l’on sait qu’il n’y aura pas de modifications ultérieures.
On peut comparer l’égalité des tuples, ils ont un ordre lexicographique naturel. On peut itérer sur les tuples comme sur toute collection (commande for).
On peut les découper en utilisant la notation []. On peut tester la présence d’un
élément x avec la syntaxe if x in tuple : ...
4.1.2
Les listes
On les forme avec la notation [ ]. Comme on voit, on peut s’en servir comme
les tuples.
23
# liste à 0 élément
t0 = []
# liste à 1 élément
t1 = [’hello’]
# liste à plusieurs éléments
t3 = [ 1, 2, 4 ]
t4 = [ 6, ’toto’, 4, ’yup’ ]
# liste de listes
tt = [ [3,2], [5,6], [4,6,7] ]
print( len( tt ) )
# donne 3
x, y, z = tt
# donne x = [3,2], y = [5,6 ], z = [4,6,7]
# On aurait pu écrire
x = tt[ 0 ]
y = tt[ 1 ]
z = tt[ 2 ]
On peut comparer l’égalité des listes, ils ont un ordre lexicographique naturel.
On peut itérer sur les listes comme sur toute collection (commande for). On
peut les découper en utilisant la notation [].
Au contraire des tuples, on peut modifier les éléments d’une liste, supprimer
des éléments, rajouter ou insérer des éléments. C’est une structure plus versatile
que le tuple.
24
l = [ 6, 4, 12 ]
l.append( 17 )
l.append( 4 )
# l = [ 6, 4, 12, 17, 4 ]
# compte le nombre de fois où 4 est dans l et l’affiche: 2
print( l.count( 4 ) )
# ajoute ces 3 éléments à l
l.extend( ( 13, 12, 6 ) )
# Recherche et renvoie l’indice d’un élément (1 ici).
print( l.index( 4 ) )
# Retourne la liste
l.reverse()
#
#
x
y
enlève le dernier élément (ou celui d’indice précisé) de la liste et
le retourne
= l.pop()
= l.pop( 2 )
# Trie les éléments
l.sort()
# Insère un élément avant l’indice donnée.
l.insert( 0, 13 )
# Crée un nouvelle liste à partir de l, en prenant tout ou une partie
ll = l[:]
ll = l[2:4]
ll = l[2:]
ll = l[:4]
On peut aussi utiliser la commande del pour éliminer des éléments d’une
liste.
25
>>>
>>>
>>>
[1,
>>>
>>>
[1,
>>>
>>>
[]
4.1.3
a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0]
a
66.25, 333, 333, 1234.5]
del a[2:4]
a
66.25, 1234.5]
del a[:]
a
Ensembles
Python propose la classe set pour représenter des ensembles, c’est-à-dire des
collections où un élément n’est présent qu’une seule et même fois. Cette classe
dispose naturellement des méthodes union, intersection, difference, . . . .
s1 = set( (3,4,5) )
s2 = set( (5,6,7) )
s3 = s1.union( s2 )
# s3 = set( 3,4,5,6,7 )
s3.discard( 4 )
# s3 = set( 3,5,6,7 )
s3.add( 8 )
# s3 = set( 3,5,6,7,8 )
if s1.issubset( s3 ):
print( ’oui’ )
s3.update( (4, 2, 8, 12 ) )
# s3 = set( 2,3,4,5,6,7,8,12 )
4 in s3
# True
N’oubliez pas qu’une chaı̂ne de caractères est aussi une structure itérable.
On peut donc passer en paramètre une chaı̂ne et chaque caractère sera donc un
élément de l’ensemble.
s1 = set( ’youpi’ )
s2 = set( ’yokaidi’ )
print( s1.union( s2 ) )
# set([’a’, ’d’, ’i’, ’k’, ’o’, ’p’, ’u’, ’y’])
26
4.1.4
Dictionnaires
Les dictionnaires ou tableaux associatifs associent à un ensemble d’éléments
(les clés) des valeurs quelconques. En Python, les clés comme les valeurs associées peuvent être de type arbitraire. On définit très simplement un nouveau
dictionnaire vide avec les accolades {}. On voit qu’on se sert d’un dictionnaire
“comme” d’une liste avec la notation crochet, sauf que l’on peut met une clé en
paramètre et non un indice entier.
>>> tel = {’jack’: 4098, ’sape’: 4139}
>>> tel[’guido’] = 4127
>>> tel
{’sape’: 4139, ’guido’: 4127, ’jack’: 4098}
>>> tel[’jack’]
4098
>>> del tel[’sape’]
>>> tel[’irv’] = 4127
>>> tel
{’guido’: 4127, ’irv’: 4127, ’jack’: 4098}
>>> tel.keys()
[’guido’, ’irv’, ’jack’]
>>> ’guido’ in tel
True
>>> for e in tel.items():
print(e)
(’jack’, 4098)
(’irv’, 4127)
(’guido’, 4127)
On peut aussi construire un dictionnaire à partir de listes de couples (clé,
valeur) ainsi
>>> dict([(’sape’, 4139), (’guido’, 4127), (’jack’, 4098)])
{’sape’: 4139, ’jack’: 4098, ’guido’: 4127}
On peut faire beaucoup plus évolué en utilisant le mécanisme des “list comprehensions”. On construit ainsi la fonction carré sur les entiers 2,4,6.
>>> dict([(x, x**2) for x in (2, 4, 6)])
{2: 4, 4: 16, 6: 36}
4.1.5
# use a list comprehension
Techniques pour parcourir les éléments de collections
(repris de www.python.org).
Pour visiter tous les éléments d’un dictionnaire, la clé et la valeur correspon27
dante peut être récupérée au même moment avec la méthode iteritems.
>>> knights = {’gallahad’: ’the pure’, ’robin’: ’the brave’}
>>> for k, v in knights.iteritems():
...
print k, v
...
gallahad the pure
robin the brave
Lorsqu’on boucle sur une séquence, la position et la valeur peut être obtenue
en même temps avec la fonction enumerate.
>>> for i, v in enumerate([’tic’, ’tac’, ’toe’]):
...
print i, v
...
0 tic
1 tac
2 toe
Pour boucler sur deux ou plus séquences en même temps, on peut les rassembler avec la fonction zip.
>>> questions = [’name’, ’quest’, ’favorite color’]
>>> answers = [’lancelot’, ’the holy grail’, ’blue’]
>>> for q, a in zip(questions, answers):
...
print ’What is your {0}? It is {1}.’.format(q, a)
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
Pour boucler sur une séquence en sens inverse, spécifier d’abord la séquence
dans le sens avant et appeler dessus la fonction reversed.
>>> for i in reversed(xrange(1,10,2)):
...
print i
...
9
7
5
3
1
Pour boucler sur une séquence dans l’ordre naturel, on peut utiliser la fonction sorted qui retournera une nouvelle liste dans le bon ordre sans modifier la
liste initiale.
28
>>> basket = [’apple’, ’orange’, ’apple’, ’pear’, ’orange’, ’banana’]
>>> for f in sorted(set(basket)):
...
print f
...
apple
banana
orange
pear
Quelques questions : Etant donnés 2 séquences s1 et s2, on veut obtenir la
séquence des éléments de s1 qui ne sont pas dans s2 et inversement.
Quelques questions : Etant donné une chaı̂ne de caractères s, écrire une fonction
qui affiche pour chaque lettre combien de fois elle est présente dans s.
>>> s = "il fait beau. la mer se prelasse. les enfants barbotent."
>>> l = set( s )
>>> print(l)
set([’a’, ’ ’, ’b’, ’e’, ’f’, ’i’, ’m’, ’l’, ’o’, ’.’, ’p’, ’s’, ’r’, ’u’, ’t’, ’n’])
>>> d = dict( (a,0) for a in l )
>>> print(d)
{’a’: 0, ’ ’: 0, ’b’: 0, ’e’: 0, ’f’: 0, ’i’: 0, ’m’: 0, ’l’: 0, ’o’: 0, ’.’: 0, ’p’: 0,
>>> for c in s:
...
d[c] = d[c] + 1
...
>>> print(d)
{’a’: 6, ’ ’: 9, ’b’: 3, ’e’: 8, ’f’: 2, ’i’: 2, ’m’: 1, ’l’: 4, ’o’: 1, ’.’: 3, ’p’: 1,
>>>
4.2
5
TODO
Introduction au langage JAVA
Le JAVA a pour principale motivation d’être portable (un programme JAVA
peut s’exécuter sur n’importe quelle machine/architecture/os du moment que
la machine virtuelle JAVA y a été installée). De plus, il s’agit d’un bon langage de programmation objet, qui dispose de presque toutes les possibilités du
paradigme objet. Il est de plus assez proche du langage C++, tout en le simplifiant sur certains points, et permet donc une adaptation assez rapide des
programmeurs C++. Enfin, il est supposé donner des programmes plus facilement maintenables, car il limite les erreurs dues à la gestion de la mémoire, et
offre aussi une bonne gestion des erreurs et exceptions. Son défaut majeur est
sans doute sa vitesse d’exécution, entre 3 et 15 fois plus lente qu’un programme
C++ similaire (selon les benchmarks). Un autre défaut est qu’il ne permet
29
pas vraiment la programmation système ou la programmation bas-niveau, tout
simplement car il introduit une couche d’abstraction entre le programme et
l’ordinateur/processeur. En quelque sorte, pour un programme JAVA, toutes
les machines sont identiques ! Nous allons présenter ici quelques points de ce
langage.
5.1
Compilation et exécution d’un programme JAVA
Vous pouvez un IDE comme Eclipse qui se chargera de ces étapes à l’aide de
quelques clics. Sinon, JAVA fournit principalement deux programmes :
javac compile un ensemble de programmes JAVA en un code que pourra interpréter la machine virtuelle. Si on dispose de deux fichiers JAVA a.java
et b.java, on pourra les compiler ainsi
> javac a.java b.java
Cela génère des fichiers a.class et b.class, qui contiennent des instructions simples exécutables par la machine virtuelle JAVA.
java l’interpréteur JAVA ou machine virtuelle JAVA. Elle exécute du bytecode
JAVA si on lui donne un point d’entrée : le nom d’une classe publique où
il y a un main, par exemple
> java a
5.2
Découpage d’un programme JAVA
En général, un programme JAVA est découpé en plusieurs fichiers sources
d’extension .java. Dans chaque fichier XXX.java se trouve une classe publique
de nom XXX. D’autres classes non publiques peuvent en faire partie. Chaque
classe contient des données membres (ou attributs) ainsi que des fonctions
membres (ou méthodes). Le corps des méthodes se situe juste après leur déclaration
et est délimité par un bloc { . . . }. Dans un tel bloc, on peut mettre des instructions.
Les fichiers JAVA sont aussi parfois regroupés en package. Si rien n’est précisé
(pas de commande package, les classes du fichiers sont dans le package par
défaut.
30
// Fichier Exemple.java
package Youpi;
import java.util.*; // importe quelques classes utiles
// Classe publique
public class Exemple {
// attribut ou donnée membre
String s;
// méthode ou fonction membre
static public void main( String[] args ) {
System.out.println( "args[0]=" + args[0] );
}
}
5.3
Types primitifs
A la base, JAVA ne manipule que les types dits primitifs : int, boolean,
char, float, double, etc. Ils permettent de coder des entiers, booléens, nombres
à virgule flottante, caractères. Pour déclarer une variable i de type int, on écrit
la ligne suivante à l’intérieur d’un bloc { . . . } :
{
...
int i; // declaration et instanciation
i = 5;
...
} // fin de la durée de vie de i.
La variable i n’existe que jusqu’à l’accolade fermante du groupe où elle a été
déclarée. On note qu’on donne à la fois un nom à la variable (ici i) et qu’on lui
associe aussi un espace mémoire pour stocker la donnée.
5.4
Autres types
On dispose ensuite de plusieurs moyens très puissants pour combiner ces
types primitifs en types complexes.
–
–
–
–
les tableaux [ ]
le regroupement des types dans une même classe, sous forme d’attributs
l’enrichissement (ou spécialisation) des types via l’héritage
la création de types génériques via les patrons ou template
On note que seules les variables de type primitif fonctionne comme les variables habituelles des langages impératifs (C, C++, Pascal, Ada). Les variables
31
d’une autre type sont appelées variables objets. On doit bien sûr les déclarer.
Ensuite, elles ne font que pointer ou désigner des objets de ce type. Il faudra
donc créer (ou allouer, ou encore instancier) des objets du type voulu à l’aide
de l’opérateur new.
5.5
Tableau d’éléments du même type primitif
Un tableau permet de stocker un nombre donné d’éléments du même type
avec un accès direct à chaque élément par le biais de son numéro (ou indice).
En JAVA, le type tableau (matérialisé par [ ]) est en fait un objet conteneur.
Il faut donc déclarer la variable tableau mais aussi créer l’espace mémoire correspondant avec l’opérateur new. Les indices vont de 0 au nombre d’éléments
moins 1, comme en C. Le nombre d’éléments ou taille du tableau est accessible
dans le champ length de la variable objet.
char[ ] s; // declaration d’une variable tableau de caracteres
s = new char[ 10 ]; // instanciation d’un tableau de 10 caracteres
s[ 0 ] = ’b’; // la lettre ’b’ est mise dans la case 0 du tableau
int i = s.length; // i recoit 10, la longueur du tableau.
Une autre façon de faire est d’initialiser le tableau avec des valeurs
// déclare et instancie un tableau de taille 6.
int[ ] t = { 1, 2, 3, 4, 5, 6 };
5.6
Regroupements à l’aide d’une Classe
On peut s’en servir un peu comme la structure C, mais il s’agit vraiment
d’une classe au sens de la programmation objet. On peut donc regrouper des
données, mais aussi associer des fonctions membres ou méthodes qui vont opérer
sur les données de la classe. On utilise le mot-clé class pour déclarer une classe.
Une classe contient essentiellement deux familles d’éléments :
– des données, appelées attributs, données membres. Chacune de ces données
est associées à chaque objet de ce type. Les données peuvent être de types
primitifs ou des classes elles-mêmes.
– des opérations, appelées méthodes, fonctions membres. Chacune de ces
opérations opèrent sur les attributs de l’objet, en utilisant éventuellement
des paramètres et en retournant éventuellement une valeur.
Voilà un exemple de classe représentant un point dans le plan.
32
class Point {
float x;
float y;
// Mets les coordonnees (x0, y0) dans le point
void init( float x0, float y0 ) {
x = x0;
y = y0;
}
// Translate ce point d’un vecteur (u,v)
void translate( float u, float v ) {
x = x + u;
y = y + v;
}
// Distance à un autre point
float distance( Point autre ) {
float dx = autre.x - x;
float dy = autre.y - y;
return Math.sqrt( dx*dx + dy*dy );
}
}
Et voilà une façon de l’utiliser :
// Fichier Exemple1.java
public class Exemple1 {
public static void main( String[ ] args ) {
Point p1; // déclare un point (sans l’instancier !)
Point p2; // déclare un autre point (sans l’instancier !)
p1 = new Point(); // créer un objet Point
p2 = new Point(); // créer un autre objet Point
p1.init( 5.0, 4.0 );
p2.init( 10.0, 3.0 );
// Distance de p1 au point p2 => on demande a p1 de la calculer.
double d12 = p1.distance( p2 );
// Distance de p2 au point p1 => on demande a p2 de la calculer.
double d21 = p2.distance( p1 );
System.out.println( d12 + " = " + d21 );
}
}
Pour compiler et exécuter ce code, on tapera dans un terminal :
> javac Exemple1.java
> java Exemple1
5.0990195135927845 = 5.0990195135927845
33
5.7
Objets et variables objet
Chaque fois que l’on appelle l’opérateur new on crée quelque part en mémoire
un nouvel élément du type spécifié, un objet donc. C’est en fait la seule et unique
manière de créer des objets en JAVA. Pour accéder à un objet, on utilise les
variables objets, qui vont pointer vers les objets créés, et qui vont nous permettre
d’accéder à leurs champs et de leur encoyer des messages. Plusieurs variables
objets peuvent donc référencer ou pointer vers le même objet.
...
Point p1 = new Point();
Point p2 = new Point();
Point p3 = p1; // p3 reference le meme objet que p1.
p1.init( 5.0, 4.0 );
p2.init( 10.0, 3.0 );
p3.x = 0.0;
System.out.println( p1.x ); // affiche 0.0 !
On accède aux champs et méthodes d’un objet via le symbole ”.” appliqué
sur une variable objet qui le référencie.
Une variable objet de type T peut désigner tout objet de type T ou d’un
sous-type de T . En JAVA, toute classe est un sous-type de la classe Object. On
peut donc toujours écrire :
...
Object o = new Point();
...
Cela a un sens, notamment lorsqu’on manipule des collections d’éléments de
types différents. On peut toujours revenir au type initial (type instancié) d’un
objet avec le transtypage.
...
Object o = new Point();
Point p1 = (Point) o; // valide
Polygon poly = (Polygon) o; // invalide, Polygon n’est pas
// un sous-type de Point
...
Une variable objet non initialisée vaut null.
34
5.8
Constructeurs d’une classe
Ce sont des méthodes particulières qui ne sont appelées qu’à la création de
l’objet (son instanciation). Cela permet de créer un objet qui a tout de suite des
données valides. Ces méthodes portent simplement le nom de la classe, peuvent
prendre des paramètres et n’ont pas de valeur de retour. Pour la classe Point,
on pourrait proposer
class Point {
...
public Point() {
x = 0.0;
y = 0.0;
}
public Point( double x0, double y0 ) {
x = x0;
y = y0;
}
public Point( Point autre ) {
x = autre.x;
y = autre.y;
}
...
}
ce qui permettrait les initialisations suivantes à l’instanciation :
...
Point p1 = new Point(); // p1.(x,y) = (0.0, 0.0)
Point p2 = new Point(3.0, 2.0); // p2.(x,y) = (3.0, 2.0)
Point p3 = new Point(p2); // p3.(x,y) = (3.0, 2.0)
...
5.9
Associations en JAVA
Une variable objet permet de pointer vers un objet JAVA. Pour relier un
objet JAVA à un autre objet JAVA, il suffit donc qu’une variable objet (du bon
type) soit déclarée comme donnée membre de la classe.
Par exemple, si tout objet de type A est relié à un objet de type B, la classe
A s’écrira :
35
class A {
...
B mon_b;
...
};
// donnee membre publique, privée, ou autre
Inversement, si l’objet de type B doit connaı̂tre cet objet de type A associé,
il faudra aussi déclarer une donnée membre dans la classe B.
class B {
...
A mon_a;
...
};
// donnee membre publique, privée, ou autre
Comme exemple, voilà une façon d’écrire une classe Segment, qui représente
un segment entre deux points.
class Segment {
Point p;
Point q;
...
double longueur() {
return p.distance( q );
}
}
Si une classe A est associée à possiblement plusieurs instances d’une classe
B ayant le même rôle, on utilisera un tableau de variables objet. Par exemple,
on peut définir un Polygone ainsi :
class Polygon {
Point[] sommets;
...
Polygon( int n ) {
// Cree un polygone regulier à n sommets
sommets = new Point[ n ];
for ( int i = 0; i < n; i++ ) {
double a = ( 2.0 * Math.PI * i ) / n;
sommets[ i ] = new Point( Math.cos( a ), Math.sin( a ) );
}
}
}
36
Quelques questions :
1. Ecrivez la classe Personne et ses relations de parenté.
2. Ecrivez les classes Homme et Femme et ses relations de parenté.
3. Y a-t-il une différence entre les attributs de type String (chaı̂ne de caractère) utilisés pour stocker le nom et prénom de la personne et les attributs désignant les objets en relation de parenté.
4. Reprenez l’exemple de la classe Fonction et proposez une classe modélisant
l’addition de deux fonctions.
5.10
Interfaces, classes abstraites
On ne s’intéresse parfois qu’aux opérations associées à un concept, et aucunement aux attributs ou au code nécessaire pour le mettre en oeuvre dans un cas
précis. JAVA offre un mécanisme pour modéliser ces abstractions, les interfaces.
Par exemple, voilà une interface possible pour les formes du plan :
interface Forme {
double aire();
double perimetre();
boolean pointInterieur( Point p );
Point centreDeGravite();
Rectangle boiteEnglobante();
}
Beaucoup d’applications et de fonctions n’ont pas vraiment besoin de savoir
comment sont écrites ces méthodes, mais ont juste besoin de les appeler. Ainsi,
elles peuvent prévoir simplement de recevoir une Forme en paramètre, et non
une spécialisation concrète.
On pourra ensuite préciser qu’une classe réalise concrètement ces opérations
avec le mot-clé implements.
class Carre implements Forme {
int m_c;
double aire() {
return m_c*m_c;
}
...
}
Une classe abstraite est spécifiée par le mot-clé abstract. Il s’agit d’un intermédiaire entre une interface et une classe, au sens ou certaines méthodes
peuvent ne pas être écrites, mais juste spécifiées. Une classe abstraite peut ou
non avoir des attributs, contrairement aux interfaces qui n’ont aucun attribut.
On note qu’aucun objet ne peut être du type d’une interface ou d’une classe
abstraite, car les objets sont concrets. En revanche les objets peuvent bien sûr
37
être des mises en oeuvre concrètes d’une ou plusieurs interfaces ou d’une classe
abstraite : le type de l’objet à l’instanciation est alors un sous-type de ces types
abstraits. Ainsi on instanciera des Carres, des Ellipses, mais jamais des Formes.
5.11
Généralisation, spécialisation, héritage
(Voir la Section 2.11.)
Le JAVA dispose de deux mécanismes de spécialisation :
– le sous-typage ou héritage d’interface : il permet de définir des spécialisations
de types existants. Dans l’approche ensembliste, il s’agit de découper un ensemble d’éléments en sous-ensembles consistants, qui offrent des opérations
supplémentaires. Il ne s’agit pas de préciser comment ces opérations sont
effectivement réalisées et quelles sont les données associées. Le sous-typage
se fait en utilisant le mot-clé implements. Une interface ou une classe peut
hériter d’autant d’interfaces qu’elle le souhaite.
– la sous-classification ou héritage de mise en œuvre : il permet d’acquérir
toutes les propriétés d’une autre classe (méthodes/opérations, données,
implémentations des méthodes), de rajouter de nouvelles méthodes et/ou
données, voire de redéfinir certaines méthodes pour les spécialiser. La sousclassification se fait en utilisant le mot-clé extends. En JAVA, seule une
classe peut hériter d’une seule autre classe. Par défaut, une classe hérite
de la classe Object. Cela induit que toute classe dérive de la classe Object
en JAVA.
On note donc que toute classe est un sous-type de Object. Le sous-typage
(et donc le polymorphisme) peut se faire via le mécanisme d’héritage d’interface
ou d’implémentation.
On réfère le lecteur aux deux exemples de la Section 2.11.
5.12
Polymorphisme
Quelques questions :
1. Reprenez l’exemple des formes. Ecrire l’interface Forme ainsi que les
différentes classes concrètes pour les formes : polygone, carré, cercle, ellipse, fonctions implicites, etc.
2. Montrez un exemple d’utilisation où on calcule l’aire totale de beaucoup
de formes de type concret différent.
6
Aperçu de la bibliothèque JAVA
On parle aussi de l’API JAVA (pour Application Programming Interface).
C’est une bibliothèque de classes déjà écrite, utilisable facilement par tout programme(ur) JAVA. Notamment, on dispose de classes pour :
38
– faire des calculs mathématiques
– gérer les fichiers
– manipuler des collections d’éléments
– faire des IHM (interfaces homme-machine avec fenêtres, boutons, et autres)
– communiquer avec une base de données
– gérer les images, vidéos, sons
– établir des connexions réseaux, communiquer sur Internet
– faire de la programmation concurrente (multithreading)
– etc
Il ne s’agit pas ici de présenter cette bibliothèque de manière exhaustive.
Nous référons le lecteur au site de Sun, qui met en-ligne toute l’API ainsi que
des tutoriaux pour rapidement savoir l’utiliser. On va juste survoler quelques
points.
39
Téléchargement