Boris Eng

R&D Engineer at OCamlPro, PhD in computer science [first name][last name]@proton.me
« Return to Teaching

Programmation orientée objet

Responsable du cours : Marc Champesme

Révision : anatomie commentée d’un programme Java

Explications * Fichier Main.java. C’est l’unique fichier qui contient la fonction principale (main). On peut l’appeler comme on veut mais c’est elle qu’on doit lancer avec la commande java Main.
1
2
3
4
5
6
7
import java.util.* // importe toutes les fonctionnalités de base de Java (dont ArrayList). On peut aussi importer un par un les modules, par exemple en faisant 'import java.util.ArrayList;'


public class Main {
    public static void main (String args[]) {
		// on met ce que fait le programme quand on le lance

    }
}
* Fichier Blabla.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class Blabla {
	/* --------------------
		Attributs
	   -------------------- */
	private int a;
	private float b;
	private String c;
	
	/* --------------------
		Constructeurs
	   -------------------- */
	/* Permet d'initialiser les objets avec des valeurs lorsqu'on
	   les créé avec 'Blabla b = new Blabla(...)'. On peut en avoir plusieurs. */
	public Blabla() {
		// 'this' se réfère à la classe actuelle, il est facultatif lorsqu'il n'y a pas d'ambiguïtés.

		this.a = 3; 
		this.b = 3.5;
		this.c = "salut";
	}
	
	// les constructeurs sont distingués par leurs paramètres

	public Blabla(int a, float b, String c) {
		// si on n'utilise pas 'this', alors il y a une confusion entre les attributs et les paramètres

		this.a = a;
		this.b = b;
		this.c = c;
	}
	
	/* --------------------
		Méthodes
	   -------------------- */
	
	/* cette fonction est appelée 'accesseur' ou 'getter'. Elle permet
	   de récupérer un attribut à partir d'un objet. On l'utilise en
	   faisant 'b.getA()' sur un objet 'b' de classe 'Blabla' */
	public int getA() {
		return this.a;
	}
	
	/* cette fonction est appelée 'mutateur' ou 'setter'. Elle permet
	   de modifier un attribut à partir d'un objet. On l'utilise en
	   faisant 'b.setA(valeur)' sur un objet 'b' de classe 'Blabla' */
	public void setA(int newVal) {
		this.a = newVal;
	}
	
	/* ----- Guide des annotations de contrat -----
	 *
	 * @invariant propriété qui ne change pas pendant l'exécution
	 * @requires  condition (sur les paramètres en général) que
	              l'on souhaite avoir avant l'exécution pour
	              assurer le bon fonctionnement de la fonction
	 * @ensures   propriété assurée par la fonction après exécution
	 * @param     nom_du_parametre	description_du_parametre
	 * @return    description du retour de la fonction
	              (quel est le résultat de la fonction)
	 */
	public int f(int x, int y) {
		// ...

		return 3;
	}
}
* Fichier BlablaNonMutable.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// les instances de cette classe ne peuvent pas être modifiées après création (on doit donc les initialiser par leur constructeur).

final class BlablaNonMutable {
	// cet attribut ne peut pas être modifié

	private final int BLABLA = 5;
	
	/* cet attribut ne peut pas être modifié mais est aussi partagé
	   entre toutes les instances (sinon chacun aurait sa propre variable) */
	private static final int BLOBLO = 3; 
	
	public final void blabla() {
		/* Cette fonction ne peut pas être redéfinie dans
		   des sous-classes par héritage à cause du mot-clé 'final' */
	}
	
}

Projet Zork

ObjetZork.java Je rappelle que this se réfère à la classe elle-même. Si on écrit this.poids, il s’agit de l’attribut poids de la classe dans laquelle on est. Vous pouvez mettre les méthodes que vous voulez selon ce que vous voulez faire ou ce dont vous avez besoin.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
 *	@invariant getNom() != null;
 *	@invariant getPoids() >= 0;
 *	@invariant getDescription() != null;
 **/

public class ObjetZork {
	private String nom;
	private int poids;
	private String description;
	private boolean transportable;
	
	/**
	 *  Initialise les objets avec un nom, une description et un poids. 
	 *  En particulier, on aimerait que le poids soit supérieur à 0.
	 * 
	 *  @param  nom           Nom de l'objet.
	 *  @param  description   Description de l'objet.
	 *  @param  poids         Poids de l'objet >= 0.
	 *  @param  transportable Indique si l'objet est transportable.
	 *
	 *	@requires	nom != null;
	 *	@requires	description != null;
	 *	@requires	poids >= 0;
	 *	@ensures	getNom() != null;
	 *	@ensures	getDescription() != null;
	 *	@ensures	getPoids() >= 0; 
	 */
	 public ObjetZork(String nom, String description, int poids, boolean transp) {
	 	this.nom = nom;
	 	this.description = description;
	 	this.poids = poids;
	 	this.transportable = transp;
	 }
	 
	 /**
	  *  Récupère le nom de l'objet.
	  *
	  *  @return  Nom de l'objet.
	  *	 @pure
	  */
	 public String getNom() {
	 	return this.nom;
	 }
	 
	 /**
	  *  Récupère la description de l'objet.
	  *
	  *  @return  Description de l'objet.
	  *	 @pure
	  */
	 public String getDescription() {
	 	return this.description;
	 }
	 
	 /**
	  *  Récupère le poids de l'objet.
	  *
	  *  @return  Poids de l'objet.
	  *	 @pure
	  */
	 public int getPoids() {
	 	return this.poids;
	 }
	 
	 /**
	  *  Indique si l'objet est transportable.
	  *
	  *  @return  Renvoie vrai si l'objet est transportable et faux sinon.
	  *	 @pure
	  */
	 public boolean estTransportable() {
	 	return this.transportable;
	 }
	 
	 /**
	  *  Affiche les détails d'un objet.
	  */
	 public void affiche() {
	 	System.out.println("Nom : " + this.nom);
	 	System.out.println("Description : " + this.description);
	 	System.out.println("Poids : " + this.poids);
	 	if (estTransportable())
	 		System.out.println("Cet objet est transportable.");
	 	else
	 		System.out.println("Cet objet n'est pas transportable.");
	 }
}
Piece.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import java.util.Map;
import java.util.EnumMap;
import java.util.ArrayList;

/**
 *  Une piece dans un jeu d'aventure. <p>
 *
 *  Cette classe fait partie du logiciel Zork, un jeu d'aventure simple en mode
 *  texte.</p> <p>
 *
 *  Une "Piece" represente un des lieux dans lesquels se déroule l'action du
 *  jeu. Elle est reliée a au plus quatre autres "Piece" par des sorties. Les
 *  sorties sont étiquettées "nord", "est", "sud", "ouest". Pour chaque
 *  direction, la "Piece" possède une référence sur la piece voisine ou null
 *  s'il n'y a pas de sortie dans cette direction.</p>
 *
 * @author     Michael Kolling
 * @author     Marc Champesme (pour la traduction francaise)
 * @version    1.2
 * @since      August 2000
 *
 *	@invariant	getDescription() != null;
 *	@invariant	getSorties() != null;
 *	@invariant	getObjets() != null;
 */

public class Piece {
 	private String description;
	// mémorise les sorties de cette piece.

	private EnumMap<Direction, Piece> sorties;
	private ArrayList<ObjetZork> objets;

	/**
	 *  Initialise une piece décrite par la chaine de caractères spécifiée.
	 *  Initialement, cette piece ne possède aucune sortie. La description fournie
	 *  est une courte phrase comme "la bibliothèque" ou "la salle de TP".
	 *
	 * @param  description  Description de la piece.
	 * @requires description != null;
	 * @ensures	 getDescription() != null;
	 * @ensures	 getSorties() != null;
	 * @ensures	 getObjets() != null;
	 */
	public Piece(String description) {
		this.description = description;
		this.sorties = new EnumMap<Direction, Piece>(Direction.class);
		this.objets = new ArrayList<ObjetZork>();
	}

	/**
	 *  Constructeur alternatif qui permet de choisir une liste d'objets
	 *  de départ.
	 *
	 * @param  description  Description de la piece.
	 * @param  objets       Liste d'objets dans la pièce.
	 *
	 * @requires	description != null;
	 * @requires 	objets != null;
	 * @ensures	getDescription() != null;
	 * @ensures getObjets() != null;
	 * @ensures getSorties() != null;	
	 */
	public Piece(String description, ArrayList<ObjetZork> objets) {
		this.description = description;
		this.sorties = new EnumMap<Direction, Piece>(Direction.class);
		this.objets = objets;
	}


	/**
	 *  Définie les sorties de cette piece. A chaque direction correspond ou bien
	 *  une piece ou bien la valeur null signifiant qu'il n'y a pas de sortie dans
	 *  cette direction.
	 *
	 * @param  nord   La sortie nord
	 * @param  est    La sortie est
	 * @param  sud    La sortie sud
	 * @param  ouest  La sortie ouest
	 *
	 * @ensures getSorties() != null;
	 */
	public void setSorties(Piece nord, Piece est, Piece sud, Piece ouest) {
		if (nord != null) {
			sorties.put(Direction.NORD, nord);
		}
		if (est != null) {
			sorties.put(Direction.EST, est);
		}
		if (sud != null) {
			sorties.put(Direction.SUD, sud);
		}
		if (ouest != null) {
			sorties.put(Direction.OUEST, ouest);
		}
	}


	/**
	 *  Renvoie la description de cette piece (i.e. la description spécifiée lors
	 *  de la création de cette instance).
	 *
	 * @return    Description de cette piece
	 * @pure
	 */
	public String descriptionCourte() {
		return description;
	}


	/**
	 *  Renvoie une description de cette piece mentionant ses sorties et
	 *  directement formatée pour affichage, de la forme: <pre>
	 *  Vous etes dans la bibliothèque.
	 *  Sorties: nord ouest</pre> Cette description utilise la chaine de caractères
	 *  renvoyée par la méthode descriptionSorties pour décrire les sorties de
	 *  cette piece.
	 *
	 * @return    Description affichable de cette piece
	 * @pure
	 */
	public String descriptionLongue() {
		return "Vous etes dans " + description + ".\n" + descriptionSorties();
	}


	/**
	 *  Renvoie une description des sorties de cette piece, de la forme: <pre>
	 *  Sorties: nord ouest</pre> Cette description est utilisée dans la
	 *  description longue d'une piece.
	 *
	 * @return    Une description des sorties de cette pièce.
	 * @pure
	 */
	public String descriptionSorties() {
		String resulString = "Sorties:";
		for (Direction sortie :  sorties.keySet()) {
			resulString += " " + sortie;
		}
		return resulString;
	}


	/**
	 *  Renvoie la piece atteinte lorsque l'on se déplace a partir de cette piece
	 *  dans la direction spécifiée. Si cette piece ne possède aucune sortie dans cette direction,
	 *  renvoie null.
	 *
	 * @param  direction  La direction dans laquelle on souhaite se déplacer
	 * @return            Piece atteinte lorsque l'on se déplace dans la direction spécifiée ou null.
	 * @pure
	 */
	public Piece pieceSuivante(Direction d) {
		return sorties.get(d);
	}
	
	/**
	 *  Ajoute des objets dans la salle.
	 *
	 * @param o L'objet que l'on souhaite ajouter.
	 * @requires o != null;
	 * @ensures this.objets.size() != 0;
	 * @ensures \exists int i; i>=0 && i<this.objets.size(); this.objets.get(i).equals(o);
	 */
	public void ajouterObjet(ObjetZork o) {
		this.objets.add(o);
	}
	
	/**
	 *  Supprime un objet spécifique dans la salle.
	 *
	 * @param o L'objet que l'on souhaite supprimer.
	 * @requires o != null;
	 * @requires this.objets.size() = \old(this.objets.size() - 1)
	 */
	public void retirerObjet(ObjetZork o) {
		this.objets.remove(o);
	}
	
	/**
	 *  Vérifie si un objet est dans la pièce.
	 *
	 * @return Vrai si la pièce contient o et sinon faux
	 * @requires o != null;
	 * @ensures \result <==> \exists int i; i>=0 && i<this.objets.size(); this.objets.get(i).equals(o);
	 */
	public boolean contient(ObjetZork o) {
		return this.objets.contains(o);
	}
	
	/**
	 *  Vérifie combien de fois un objet est dans une pièce.
	 *
	 * @return Le nombre de fois que l'objet 'o' est dans la pièce
	 * @requires o != null;
	 * @ensures \result > 0 <==> \exists int i; i>=0 && i<this.objets.size(); this.objets.get(i).equals(o);
	 * @pure
	 */
	public int contientCombienDe(ObjetZork o) {
		int nb = 0;
		for (ObjetZork obj : this.objets) {
			if (obj.equals(o))
				nb++;
		}
		return nb;
	}
	
	/**
	 *  Affiche la liste des objets dans la salle.
	 */
	public void afficheObjets() {
		System.out.println("Voici la liste des objets dans la salle :");
		for (ObjetZork o : this.objets) {
			if (o != null)
				o.affiche();
			System.out.println("-------------------------------------");
		}
	}
}
Equals & Hashcode pour ObjetZork.java Il est parfois utile de redéfinir la méthode equals qui permet de dire si deux objets sont égaux pour la personnaliser et choisir nous-même ce que signifie être égal pour deux objets. Par exemple :
1
2
3
4
5
6
7
	@Override public boolean equals(Object obj) {
		return this.poids == obj.poids &&
		this.description.equals(obj.description) &&
		this.nom.equals(obj.nom) &&
		this.transportable == obj.transportable;
	}
	
Tester l’égalité peut être coûteux et inefficace dans certains cas où on doit faire énormément de comparaisons. Pour cette raison, Java stocke un code dans les objets qui s’appelle hashcode. Le principe est que si deux objets sont censés être égaux alors ils doivent avoir le même hashcode (mais la réciproque n’est pas forcément vraie, on a pas besoin d’unicité). Pour redéfinir le hashcode, doit respecter le “contrat” qui donne des indications sur ce qu’on doit respecter pour la définition. Pour des raisons techniques, dans la documentation Java, il est souvent choisi de multiplier le hashcode des chaînes de caractère par 31 (je rappelle que les chaînes de caractère sont des objets). Similairement, pour les booléens true et false, on choisit habituellement 1231 et 1237. On pourrait donc avoir le code suivant :
1
2
3
4
5
	@Override public int hashCode() {
		return this.poids + 31*this.description.hashCode() + 31*this.nom.hashCode() +
		(this.transportable ? 1231 : 1237);
	}
	
Joueur.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import java.util.Map;
import java.util.EnumMap;
import java.util.ArrayList;

/**
 *	@invariant this.poids_sac < this.POIDS_MAX;
 **/

public class Joueur {
	private ArrayList<ObjetZork> sac;
	private int poids_sac;
	private final int POIDS_MAX = 50;

	/**
	 *  Initialise un joueur avec un sac vide par défaut.
	 *  @ensures this.poids_sac = 0;
	 *  @ensures this.sac != null;
	 */
	public Joueur() {
		this.poids_sac = 0;
		this.sac = new ArrayList<ObjetZork>();
	}

	/**
	 *  Constructeur alternatif qui permet de choisir une liste d'objets
	 *  de départ dans le sac du joueur.
	 *
	 * @param  objets       Liste d'objets dans la pièce.
	 * 
	 * @requires this.objets != null;
	 * @ensures  this.poids_sac >= 0;
	 */
	public Joueur(ArrayList<ObjetZork> objets) {
		this.sac = objets;
		for (ObjetZork o : objets)
			this.poids_sac += o.getPoids();
	}
	
	/**
	 *  Ajoute des objets dans le sac du joueur.
	 *
	 * @param o L'objet que l'on souhaite ajouter.
	 * @requires o != null;
	 * @ensures this.possede(o);
	 */
	public void ajouterObjet(ObjetZork o) {
		this.sac.add(o);
	}
	
	/**
	 *  Supprime un objet spécifique dans le sac du joueur.
	 *
	 * @param o L'objet que l'on souhaite supprimer.
	 * @requires o != null;
	 * @ensures !this.possede(o);
	 */
	public void retirerObjet(ObjetZork o) {
		this.sac.remove(o);
	}
	
	/**
	 *  Vérifie si un objet est possédé par le joueur.
	 *
	 * @return Vrai si le sac contient o et sinon faux
	 * @requires o != null;
	 * @ensures \result <==> \exists int i; i>=0 && i<this.objets.size(); this.objets.get(i).equals(o);
	 */
	public boolean possede(ObjetZork o) {
		return this.sac.contains(o);
	}
	
	/**
	 *  Vérifie combien de fois un objet est dans le sac du joueur.
	 *
	 * @return Le nombre de fois que l'objet 'o' est dans le sac
	 * @requires o != null;
	 * @ensures \result>=0 <==> \exists int i; i>=0 && i<this.objets.size(); this.objets.get(i).equals(o);
	 */
	public int contientCombienDe(ObjetZork o) {
		int nb = 0;
		for (ObjetZork obj : this.sac) {
			if (obj.equals(o))
				nb++;
		}
		return nb;
	}
	
	/**
	 *  Affiche la liste des objets dans le sac.
	 */
	public void afficheObjets() {
		System.out.println("Voici la liste des objets dans votre sac :");
		for (ObjetZork o : this.sac) {
			if (o != null)
				o.affiche();
			System.out.println("-------------------------------------");
		}
	}
	
	/**
	 *  Permet de porter un objet et le mettre dans son sac.
	 * 
	 *  @param o L'objet que l'on souhaite porter
	 *  @requires o != null;
	 *  @ensures this.possede(o);
	 */
	public void porter(ObjetZork o) {
		if (o.estTransportable()) {
			if (o.getPoids()+this.poids_sac <= this.POIDS_MAX) {
				this.poids_sac += o.getPoids();
				this.ajouterObjet(o);	
			}
		}		
		else
			System.out.println("Cet objet ne peut pas être transporté. ");					
	}
}
    

TP formes géométriques (Polygone)

Détails On veut créer des classes pour plusieurs formes géométriques en utilisant l'héritage :
1
2
3
4
5
public class Polygone {...}
public class Quadrilatere extends Polygone {...}
public class Rectangle extends Quadrilatere {...}
public class Carre extends Rectangle {...}
public class Triangle extends Polygone {...}
Voici pour chaque classes les attributs et méthodes que vous devez avoir (n'oubliez pas d'ajouter les invariants et contrats en commentaires) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Polygone {
	private Point2D[] mesSommets;
	
	public Polygone(List<Point2D> lp) { ... } // constructeur

	public int getNbSommets() { ... } // récupère le nombre de sommets

	public Point2D getSommet(int i) { ... } // récupère le sommmet d'indice i

	public Line2D getCote(int i) { ... } // récupère le côté d'indice i

	public double lgCote(int i) { ... } // récupère la longueur du côté d'indice i

	public double perimetre() { ... } // renvoie le perimètre du polygone

	public void translate(double dx, double dy) { ... } // déplace la forme

	public void symetrieOx() { ... } // renverse la forme sur l'axe x

	public void symetrieOy() { ... } // renverse la forme sur l'axe y

	public Polygone addSommet(Point2D p) { ... } // ajoute un sommet

	@Override public boolean equals(Object o) { ... }
	@Override public Polygone clone() { ... }
	@Override public int hashCode() { ... }
	@Override public String toString() { ... } // affiche les informations du polygone

 
}
1
2
3
4
public class Quadrilatere extends Polygone {
	// constructeur

	public Quadrilatere(Point2D p1, Point2D p2, Point2D p3, Point2D p4) { ... }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Rectangle extends Quadrilatere {
    private double lgH;
    private double lgV;

	// constructeurs

    public Rectangle(Point2D p0, Point2D p2) { ... }
    public Rectangle(Point2D p0, double lgH, double lgV) { ... }

    public double lgCoteH() { ... } // récupère le côté horizontal

    public double lgCoteV() { ... } // récupère le côté vertical


    @Override public double perimetre() { ... }
    public double surface() { ... } // calcul l'aire du rectangle

}
1
2
3
4
5
6
7
public class Carre extends Rectangle {
	// constructeur

    public Carre(Point2D p0, double cote) { ... }

    public double lgCote() { ... } // récupère la longueur des côtés

    @Override public double perimetre() { ... }
}
1
2
3
4
public class Triangle extends Polygone {
	// constructeur

    public Triangle(Point2D p0, Point2D p1, Point2D p2) { ... }
}

TP Zork et héritage avec exceptions

Héritage

Il faut d’abord prendre le fichier suivant qui définit ce qu’est un conteneur et l’ajouter au projet Java :

Conteneur.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
 * Conteneur d'ObjetZork. Les éléments contenus ne sont pas ordonnés et un
 * conteneur peut contenir plusieurs exemplaires d'un même ObjetZork (au sens de
 * equals).
 *
 * @author Sujeevan ASEERVATHAM
 * @author Marc Champesme
 * @version 3 janvier 2021
 * @since 29 mars 2007
 * 
 * @invariant !contient(null);
 */
public interface Conteneur {

	/**
	 * Renvoie le nombre d'exemplaires de l'objet specifié presents dans ce
	 * conteneur. La presence d'un objet est testée en utilisant la méthode equals
	 * de la classe ObjetZork.
	 *
	 * @param oz Objet dont on cherche à connaitre le npmbre d'exemplaires.
	 * @return le nombre d'exemplaires de l'objet specifié.
	 * 
	 * @ensures \result >= 0;
	 * @ensures contient(oz) <==> \result > 0;
	 * @ensures !contient(oz) <==> \result == 0;
	 * @ensures \result <= getNbObjets();
	 * 
	 * @pure
	 */
	public int contientCombienDe(Object oz);

	/**
	 * Renvoie true si le conteneur contient au moins un exemplaire de l'objet
	 * specifié. La présence d'un objet est testée en utilisant la méthode equals de
	 * la classe ObjetZork. L'argument doit être non <code>null</code>.
	 *
	 * @param oz Objet dont on cherche à savoir s'il est present.
	 * @return true si ce conteneur possede au moins un exemplaire de l'objet
	 *         specifié ; false sinon.
	 * 
	 * @ensures (getNbObjets() == 0) ==> !\result;
	 * @ensures (contientCombienDe(oz) > 0) ==> \result;
	 * 
	 * @pure
	 */
	public boolean contient(Object oz);

	/**
	 * Renvoie le nombre d'ObjetZork dans ce conteneur. Chaque occurence de chaque
	 * objet est comptée.
	 *
	 * @return Le nombre d'ObjetZork dans ce conteneur.
	 * 
	 * @ensures \result >= 0;
	 * 
	 * @pure
	 * 
	 */
	public int getNbObjets();

	/**
	 * Retourne une nouvelle instance de tableau d'ObjetZork, dont la taille est le
	 * nombre d'éléments de ce conteneur. Le tableau renvoyé contient tous les
	 * ObjetZork contenus dans ce conteneur.
	 *
	 * @return Un tableau d'ObjetZork contenant tous les éléments de ce conteneur..
	 * 
	 * @ensures \result != null;
	 * @ensures \result.length == getNbObjets();
	 * @ensures (\forall int i; i >= 0 && i < \result.length; contient(\result[i]));
	 * 
	 * @pure
	 */
	public ObjetZork[] getTabObjets();

	/**
	 * Indique si l'ObjetZork peut être ajouté dans ce conteneur
	 *
	 * @param oz L'objet à ajouter.
	 * @return true si l'objet peut être ajouté ; false sinon.
	 * 
	 * @ensures (oz == null) ==> !\result;
	 * 
	 * @pure
	 */
	public boolean ajoutPossible(ObjetZork oz);

	/**
	 * Ajoute l'objet specifié au conteneur. Si l'objet est déjà présent, un
	 * exemplaire supplémentaire de cet objet y est ajouté. La presence d'un objet
	 * est testée en utilisant la methode equals de la classe ObjetZork.
	 *
	 * @param oz L'objet à ajouter dans ce conteneur
	 * 
	 * @throws NullPointerException     si l'élément spécifié est null
	 * @throws IllegalArgumentException si certaines propriétés de l'objet spécifié
	 *                                  empèchent son ajout
	 * 
	 * @requires ajoutPossible(oz);
	 * @ensures contient(oz);
	 * @ensures contientCombienDe(oz) == \old(contientCombienDe(oz)) + 1;
	 * 
	 */
	public void ajouter(ObjetZork oz);

	/**
	 * Retire un exemplaire de l'objet specifié du conteneur si cet objet y est
	 * present. Renvoie true si cet objet était effectivement présent dans ce
	 * conteneur et que l'objet a pu être effectivement retiré ; renvoie false
	 * sinon. La présence d'un objet est testée en utilisant la méthode equals de la
	 * classe ObjetZork. 
	 *
	 * @param oz Objet dont un exemplaire doit être retirer de ce conteneur
	 * @return true si cet objet etait effectivement present ; false sinon.
	 * 
	 * 
	 * @ensures \old(contient(oz)) <==> \result;
	 * 
	 * @ensures \old(contient(oz)) 
	 * 			<==> (contientCombienDe(oz)  == \old(contientCombienDe(oz)) - 1);
	 * @ensures \old(contientCombienDe(oz) <= 1) ==> !contient(oz);
	 * @ensures \old(contientCombienDe(oz) > 1) <==> contient(oz);
	 * 
	 */
	public void retirer(Object oz);

	/**
	 * Renvoie true si le Conteneur spécifié est non <code>null</code> et contient
	 * les mêmes ObjetZork (au sens de equals) en autant d'exemplaires.
	 *
	 * @param c Le Conteneur à comparer avec ce conteneur.
	 * @return true si le Conteneur spécifié contient les mêmes ObjetZork en autant
	 *         d'exemplaires
	 * 
	 * @old ObjetZork[] thisTab = this.getTabObjets();
	 * @ensures (c == null) ==> !\result;
	 * @ensures (getNbObjets() != c.getNbObjets()) ==> !\result;
	 * @ensures \result <==> (c != null)
	 * 		&& (getNbObjets() == c.getNbObjets())
	 * 		&& (\forall int i; i >= 0 && i < getNbObjets(); 
	 * 				this.contientCombienDe(thisTab[i]) == c.contientCombienDe(thisTab[i]));
	 * 
	 * @pure
	 * 
	 */
	public boolean memeContenu(Conteneur c);

}

Il faut ensuite développer les classes suivantes :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class ArrayConteneur implements Conteneur {
	private ObjetZork[] lesObjets;
	private int nbObjets;
	public ArrayConteneur() { }
	public ArrayConteneur(ObjetZork[] objets, int nbObjets) { }
	@Override public int contientCombienDe(Object oz) { }
	@Override public boolean contient(Object oz) { }
	@Override public int getNbObjets() { }
	@Override public ObjetZork[] getTabObjets() { }
	@Override public void ajouter(ObjetZork oz) { }
	@Override public void retirer(Object oz) { }
	@Override public boolean memeContenu(Conteneur c) { }
	@Override public abstract boolean ajoutPossible(ObjetZork oz);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.*;

public abstract class ArrayListConteneur implements Conteneur {
	private ArrayList<ObjetZork> lesObjets;
	public ArrayListConteneur() { }
	public ArrayListConteneur(Collection<ObjetZork> objets) { }
	@Override public int contientCombienDe(Object oz) { }
	@Override public boolean contient(Object oz) { }
	@Override public int getNbObjets() { }
	@Override public ObjetZork[] getTabObjets() { }
	@Override public void ajouter(ObjetZork oz) { }
	@Override public void retirer(Object oz) { }
	@Override public boolean memeContenu(Conteneur c) { }
	@Override public abstract boolean ajoutPossible(ObjetZork oz);
}

On termine en modifiant Joueur.java et Piece.java de sorte à ce qu’ils héritent de ArrayListConteneur pour pouvoir contenir des ObjetZork de façon uniforme.

Exceptions

On ajoute des activations d’erreurs avec throw. Typiquement, on les utilise dans les cas où les conditions de @requires sont invalides. Par exemple :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	/**
	 * Initalise un joueur ne transportant aucun objet et dont le nom est la
	 * chaîne de caractères spécifiée. Le poids maximal est initialisé à 10.
	 *
	 * @requires nom != null;
	 * @ensures getNom().equals(nom);
	 * @ensures getPoidsMax() == 10;
	 * @ensures getPoids() == 0;
	 * @ensures getNbObjets() == 0;
	 *
	 * @param nom	Le nom du joueur
	 *
	 * @throws NullPointerException	si le nom spécifié est null
	 */
	public Joueur(String nom) {
		if (nom == null) {
			throw new NullPointerException("Le nom du Joueur doit être non null");
		}
		this.nom = nom;
		poidsMax = 10;
	}

Regarder le cours pour plus d’informations sur les types d’erreurs : https://moodlelms.univ-paris13.fr/mod/resource/view.php?id=97740

Généricité

Les conteneurs ne peuvent manipuler que des ObjetZork. Cependant, ils pourraient très bien traiter n’importe quels types d’objets de façon générique.

Modifier ArrayListConteneur (et pourquoi pas ArrayConteneur si vous voulez) pour traiter n’importe quel type d’objet et utiliser cette classe pour gérer des ObjetZork dans Joueur et Piece.

Tester avec une fonction main.

PS : je viens d’inventer le sujet un peu avant le TP car je savais pas trop quoi donner donc peut-être que ça va changer.