Licence Info 3éme année - module Programmation Objet 2 Examen – janvier 2018 – session 1 – durée : 2h
Documents autorisés : les polycopiés du cours (pas de TD/TP)
Partie A : Programmation graphique (environ 10 points)
L’objectif est de créer une interface avec MVC pour simuler un jeu de devination d’un password codé sous forme de n bits dont la valeur est 0 ou 1.
L’interface est donnée ci-dessous, où le nombre de bits saisi est 3, le bouton «Set a password » permet de gérer au hazard un entier (110) entre 0 et 2^3 comme password, et l’utilisateur fait une proposition, si cette proposition est bonne, la vérification va afficher « Yes » ; sinon
« No ».
Question A.1 :
Qu’est-ce que le design pattern MVC ? Définir le MVC pour cet exercice.
Question A.2 : Coder ce MVC.
Quelques fonctions dont vous avez besoin éventuellement : Math.pow(2,n) // 2 ^ n
x=(int)(Math.random()*m) // générer un entier au hazard entre 0 et m-1
Integer.parseInt(mot,2); // convertir un entier binaire sous forme de chaîne de caractères en entier décimal
Partie 2 : Casier pour ranger des objets … généricité, Exception, thread, …..
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class Casier {
private ArrayList contenu;
public Casier(int taille) {
contenu = new ArrayList(taille);
for (int i = 0; i < taille ; ++i) contenu.add(null);
}
public Object get(int position) { return contenu.get(position-1);
}
public void ranger(int position, Object element) { if (this.estVide(position))
contenu.set(position-1, element);
}
public Object retirer(int position) {
return contenu.set(position-1, null); // set renvoie l'élément précédemment stoké }
public boolean estVide(int position) { return (contenu.get(position-1) == null);
}
public void sauvegarder(String nomFichier) throws IOException { File fichierPersistant = new File(nomFichier);
if ((fichierPersistant.exists() && fichierPersistant.isFile() && fichierPersistant.canWrite())
|| fichierPersistant.createNewFile()) {
ObjectOutputStream flotEcriture = new ObjectOutputStream(new
FileOutputStream(fichierPersistant));
flotEcriture.writeObject(this);
flotEcriture.close();
} else
throw new IOException ("impossible de Sauvegarder");
}
public void restaurer(String nomFichier) throws IOException { File fichierPersistant = new File(nomFichier);
try {
if (fichierPersistant.exists() && fichierPersistant.isFile() && fichierPersistant.canRead()) {
ObjectInputStream flotLecture = new ObjectInputStream(new
FileInputStream(fichierPersistant));
Casier sauvegarde = (Casier)flotLecture.readObject();
this.contenu = sauvegarde.contenu ; flotLecture.close();
} else
throw new IOException ("impossible de Restaurer");
} catch (ClassNotFoundException cnfe) {
throw new IOException ("impossible de Restaurer");
} }
public String toString() {
StringBuffer resultat = new StringBuffer();
for (int i = 1; i <= this.contenu.size() ; ++i) { resultat.append("case["+i+";]=");
if (! this.estVide(i))
resultat.append(this.get(i));
resultat.append("\n");
}
return resultat.toString();
} }
Et l'interface graphique la classe ManipulationCasierDeMot : Ci-contre une exécution
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
public class ManipulationCasierDeMot extends JFrame { private final int TAILLE = 9;
private final String SAUVEGARDE = ".monEnsemble";
private Casier monCasier;
private JTextField elementMot;
private JSpinner numeroCase;
private JTextArea resultat;
private JLabel etatOperation;
public ManipulationCasierDeMot() { super();
monCasier = new Casier(TAILLE);
Box boiteTout = new Box(BoxLayout.Y_AXIS);
Box boiteOperation = new Box(BoxLayout.X_AXIS);
JButton boutonRanger = new JButton("Ranger");
boiteOperation.add(boutonRanger);
boutonRanger.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { etatOperation.setText(" ");
String sansBlanc = elementMot.getText().trim();
if (sansBlanc.length()>0) {
monCasier.ranger((int)numeroCase.getValue(), sansBlanc);
resultat.setText(monCasier.toString());
} else
etatOperation.setText("erreur mot vide ");
elementMot.setText("");
} });
JButton boutonRetirer = new JButton("Enlever");
boiteOperation.add(boutonRetirer);
boutonRetirer.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {
etatOperation.setText(" ");
String element = monCasier.retirer((int)numeroCase.getValue());
elementMot.setText((element == null)?"":element);
resultat.setText(monCasier.toString());
} });
boiteTout.add(boiteOperation);
Box boiteParametres = new Box(BoxLayout.X_AXIS);
boiteParametres.add(new JLabel("num : "));
numeroCase = new JSpinner(new SpinnerNumberModel(1,1,9,1));
boiteParametres.add(numeroCase);
boiteParametres.add(new JLabel(" élément : "));
elementMot = new JTextField(" ");
boiteParametres.add(elementMot);
boiteTout.add(boiteParametres);
resultat = new JTextArea(TAILLE, 10);
resultat.setEditable(false);
resultat.setText(monCasier.toString());
boiteTout.add(resultat);
Box boiteSauverRestaurer = new Box(BoxLayout.X_AXIS);
JButton boutonSauver = new JButton("Sauvegarder");
boutonSauver.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { etatOperation.setText(" ");
Thread sauver = new Thread(new Sauvegarder(SAUVEGARDE));
sauver.start();
} });
boiteSauverRestaurer.add(boutonSauver);
JButton boutonRestaurer = new JButton("Restaurer");
boutonRestaurer.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {
etatOperation.setText(" ");
Thread restaurer = new Thread(new Restaurer(SAUVEGARDE));
restaurer.start();
try {
restaurer.join();
} catch (InterruptedException e1) {
etatOperation.setText("impossible de Restaurer");
} } });
boiteSauverRestaurer.add(boutonRestaurer);
boiteTout.add(boiteSauverRestaurer);
etatOperation = new JLabel(" ");
boiteTout.add(etatOperation);
this.setContentPane(boiteTout);
this.setDefaultCloseOperation(getDefaultCloseOperation());
this.pack();
this.setVisible(true);
}
class Sauvegarder implements Runnable { private String nomFichier;
private boolean etat ;
public Sauvegarder(String nomFichier) { this.nomFichier = nomFichier;
}
public void run() { try {
monCasier.sauvegarder(nomFichier);
etatOperation.setText("sauvegarde réussie");
} catch (IOException ioe) {
etatOperation.setText("erreur de sauvegarde ");
} }
}
class Restaurer implements Runnable { private String nomFichier;
private boolean etat ;
public Restaurer(String nomFichier) { this.nomFichier = nomFichier;
}
public void run() { try {
monCasier.restaurer(nomFichier);
resultat.setText(monCasier.toString());
etatOperation.setText("restauration réussie");
} catch (IOException ioe) {
etatOperation.setText("erreur de restauration ");
} }
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() {
new ManipulationCasierDeMot();
} });
} }
Les questions sont indépendantes !
Question A :
La programmation événementielle des boutons "Sauvegarder" et "Restaurer" tient compte que les lectures et écritures dans un fichier sont des opérations longues, et que les opérations longues ne doivent pas monopoliser le thread graphique.
La programmation du bouton "Sauvegarder" lance donc un thread qui réalise l'écriture dans un fichier.
Malheureusement l'utilisateur peut continuer de manipuler le casier pendant ce même temps.
Il serait souhaitable de faire une copie qui sera sauvegardée. Le code serait donc :
class Sauvegarder implements Runnable { private String nomFichier;
private boolean etat ; private Casier copie;
public Sauvegarder(String nomFichier) { this.nomFichier = nomFichier;
try {
this.copie = (Casier)monCasier.clone() ; } catch (CloneNotSupportedException cnse) {
etatOperation.setText("erreur de sauvegarde ");
} }
public void run() { try {
copie.sauvegarder(nomFichier);
etatOperation.setText("sauvegarde réussie");
} catch (IOException ioe) {
etatOperation.setText("erreur de sauvegarde ");
} }
}
Que faut-il ajouter et/ou modifier dans la classe Casier ?
Question B :
Attention à ce que les opérations "graphiques" soient bien traitées par le thread graphique.
En regardant précisément le code du bouton "Restaurer", les composants graphiques "etatOperation" et
"resultat" sont manipulés par un thread autre que l'event-dispatcher.
Proposez une solution programmée.
Question C :
J'essaye le bouton Sauvegarder …. Catastrophe !
java.io.NotSerializableException: Casier
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at Casier.sauvegarder(Casier.java:38)
at ManipulationCasierDeMot$Sauvegarder.go(ManipulationCasierDeMot.java:110) at ManipulationCasierDeMot$3.actionPerformed(ManipulationCasierDeMot.java:77) ...