Créez le modèle de données
Avant de débuter la conception de la couche Données de l’architecture de notre application, jetons un coup d'œil à la maquette ci-dessous, afin de comprendre comment doit fonctionner le quiz de notre application.
En cliquant sur une réponse (1), l’interface se met à jour pour indiquer à l’utilisateur si la réponse est bonne ou non, et le bouton "Suivant" apparaît (2).
En cliquant sur le bouton “Next” (2), l’interface se met à jour pour afficher une nouvelle question. À nouveau l’utilisateur clique sur une réponse (3), etc.
Lorsque l’utilisateur a répondu à la dernière question, le bouton “Finish” apparaît (4).
En cliquant sur le bouton “Finish”, une fenêtre de dialogue apparaît pour indiquer à l’utilisateur son score final (5). En cliquant sur “Quit”, le fragment
QuizFragment
est remplacé par le fragmentWelcomeFragment
.
Pour concevoir cette fonctionnalité, nous devons modéliser l’objet représentant une question du quiz. Cet objet se caractérise par :
la question représentée par une chaîne de caractères ;
les propositions représentées par une liste de chaînes de caractères ;
la bonne réponse, représentée par un entier correspondant à l’index de proposition correcte dans la liste précédente.
Pour modéliser cet objet, nous allons créer une classe Java nommée Question
.
Pour créer un nouveau package data
dans Android Studio, faites un clic droit sur le package principal de votre projet au sein de l’arborescence de fichier, puis allez dans le menu New et sélectionnez Package.
Une fenêtre apparaît alors, vous permettant de saisir le nom du nouveau package. Complétez le chemin actuel en ajoutant à la fin “data”.
Dans ce package, nous allons créer une classe Question
. Pour cela, dans l’arborescence de fichier, faites un clic droit sur le package data
, puis allez dans le menu New et cliquez sur Java Class. Saisissez “Question” dans la fenêtre qui apparaît puis tapez sur Entrée .
L'instance de cette classe contiendra une question, avec les quatre réponses associées et la bonne réponse correspondante. De ce fait, elle va contenir les trois attributs privés suivants :
private final String question;
private final List<String> choiceList;
private final int answerIndex;
Pensez bien à importer la classe Java List
comme ceci : import java.util.List;
.
Nous allons maintenant créer le constructeur et les fonctions getters et setters nécessaires pour cette classe. Pour rappel, pour une variable privée donnée, une fonction de type getter va permettre d’accéder à sa valeur, et une fonction de type setter de la modifier. Le constructeur va nous permettre de créer un objet de type Question
dans notre code.
Ne foncez pas tête baissée pour écrire à la main toutes ces fonctions. En effet, Android Studio vous permet de générer toutes ces fonctions automatiquement. Pour cela, positionnez le curseur de votre souris sur le nom de la classe Question
, puis faites un clic droit ou appuyez sur ALT + Insert sur PC, ou ⌘ + N sur Mac. Sélectionnez Getter and Setter. Dans la fenêtre qui s’affiche, sélectionnez les 3 variables et appuyez sur OK.
Renouvelez l’opération en sélectionnant cette fois-ci Constructor dans la liste, en prenant soin de bien sélectionner les trois variables de la classe Question
en tant que paramètres du constructeur.
Vous devriez à la fin obtenir le code ci-dessous pour la classe Question
:
public class Question {
private final String question;
private final List<String> choiceList;
private final Integer answerIndex;
public Question(String question, List<String> choiceList, int answerIndex) {
this.question = question;
this.choiceList = choiceList;
this.answerIndex = answerIndex;
}
public String getQuestion() {
return question;
}
public List<String> getChoiceList() {
return choiceList;
}
public Integer getAnswerIndex() {
return answerIndex;
}
}
Chouette, nous avons maintenant une classe Java qui va nous permettre de représenter l’objet principal de notre quiz. Mais où sont les données ?
C’est justement l’objectif de la prochaine section !
Récupérez les données
Les données d’une application peuvent provenir de différentes sources :
un fichier ;
une base de données locale (par exemple via la bibliothèque Room) ;
une API externe (par exemple via la bibliothèque Retrofit).
Pour notre application, bien que dans la réalité, nous aurions certainement fait appel à une API pour récupérer une liste de questions aléatoires, pour des raisons de simplicité, nous allons créer une banque de questions “en dur” dans une classe Java.
Pour ce faire, dans le package data
, nous allons créer une classe Java nommée QuestionBank
. Elle va exposer une fonction getQuestions()
qui retournera une liste de questions prédéfinie. Ci-dessous la classe que j'ai développée. Vous verrez, mes questions ne sont pas très fun, alors n’hésitez pas à inventer vos propres questions.
public class QuestionBank {
public List<Question> getQuestions() {
return Arrays.asList(
new Question(
"Who is the creator of Android?",
Arrays.asList(
"Andy Rubin",
"Steve Wozniak",
"Jake Wharton",
"Paul Smith"
),
0
),
new Question(
"When did the first man land on the moon?",
Arrays.asList(
"1958",
"1962",
"1967",
"1969"
),
3
),
new Question(
"What is the house number of The Simpsons?",
Arrays.asList(
"42",
"101",
"666",
"742"
),
3
),
new Question(
"Who painteddid the Mona Lisa paint?",
Arrays.asList(
"Michelangelo",
"Leonardo Da Vinci",
"Raphael",
"Caravaggio"
),
1
),
new Question(
"What is the country top-level domain of Belgium?",
Arrays.asList(
".bg",
".bm",
".bl",
".be"
),
3
)
);
}
private static QuestionBank instance;
public static QuestionBank getInstance() {
if (instance == null) {
instance = new QuestionBank();
}
return instance;
}
}
Si nous reprenons le schéma précédent, nous avons maintenant la couche Données qui contient nos fameuses questions grâce à la classe QuestionBank
.
OK on avance, maintenant nous avons des données dans notre couche Données. Mais comment fait-on pour y accéder depuis le ViewModel ?
Pour exposer les données dont l’application a besoin, nous allons utiliser le pattern Repository (oui, je sais, encore un pattern…). Voyons à quoi il va servir.
Le pattern Repository
Ce pattern consiste à utiliser une classe intermédiaire pour servir de médiateur entre le ViewModel et les différentes sources de données.
Mais en quoi avons-nous besoin de médiation ? Les données sont là, ne peut-on pas les envoyer telles quelles en utilisant QuestionBank directement depuis le ViewModel ?
On pourrait effectivement utiliser QuestionBank
directement depuis le ViewModel. Mais ici, on va apprendre à faire les choses comme dans la vraie vie de développeur ou développeuse en entreprise.
Pour comprendre la nécessité d’un médiateur tel que le Repository, revenons à notre couche Données. Bien que dans notre cas les données proviennent d’une source unique via le fichier QuestionBank
, il est courant dans une application de devoir combiner plusieurs sources de données. Typiquement, une base de données locale et une API externe.
Pour l’application SuperQuiz, nous pourrions par exemple imaginer dans le futur de récupérer des questions sur internet depuis un serveur, et de les stocker dans une base de données locale sur le téléphone. Cela faciliterait par exemple l’utilisation de l’application hors connexion. C’est l’une des raisons qui justifie l’usage du pattern Repository.
Un Repository permet donc de :
centraliser l’accès aux données ;
gérer à un endroit unique la logique permettant de définir quelle source de données utiliser.
Si nous reprenons notre schéma, cela donne ça :
À vous de jouer !
Maintenant, à vous de créer le Repository pour nos questions !
Dans le package data
, créez une classe Java intitulée QuestionRepository
.
Celle-ci prend en paramètre la source de données unique QuestionBank
. N’oubliez pas de déclarer le constructeur de cette classe.
Enfin, faites en sorte que cette classe expose une fonction getQuestions()
qui retourne la liste des questions issues de la banque de questions.
Essayez seul(e), avant de regarder la solution ci-dessous.
public class QuestionRepository {
private final QuestionBank questionBank;
public QuestionRepository(QuestionBank questionBank) {
this.questionBank = questionBank;
}
public List<Question> getQuestions() {
return questionBank.getQuestions();
}
}
En résumé
Pour concevoir la couche Données d’une fonctionnalité, il faut identifier les modèles de données spécifiques à l’application, et créer un objet Java pour chaque modèle.
Il est d’usage de positionner tous les fichiers relatifs à la couche Données au même endroit, par exemple dans un package
data
.Les données d’une application peuvent provenir de différentes sources : un fichier, une base de données locale, une API externe, etc.
Une fois la provenance des données déterminée, la logique permettant d’arbitrer quelles données utiliser se trouve dans une classe de type Repository.
Le Repository est le seul à pouvoir accéder aux données.
Il convient de créer un Repository par type de donnée dont l’application a besoin.
Notre couche Données est maintenant développée, et la fonction getQuestions()
du Repository QuizRepository
est prête à être appelée depuis le ViewModel associé à l’écran de quiz. Nous allons maintenant coder la logique métier spécifique à notre fonctionnalité de quiz dans le ViewModel associé.