Contrôle continu 2024-25
1. Refactorings
1.1. Définitions
Expliquez la différence entre une opération de refactoring et l'activité de refactoring
Solution
Une opération de refactoring est une transformation de programme source, qui préserve son comportement visible.
L’activité de refactoring consisté à améliorer la conception du code source d’un logiciel, sans modifier son comportement visible, grâce à l’application d’opération de refactorings.
1.2. Spécialisation d’un champ
Expliquez le fonctionnement de l’opération de refactoring "Spécialisation d’un champ" (Push Down Field)
Solution
Cette opération déplace un champ d’une super-classe mère vers toutes ses sous-classes.
1.3. Préconditions
Quelles sont les pré-conditions nécessaires pour l’application de l’opération de refactoring "Spécialisation d’un champ" (Push Down Field), dans le contexte d’un programme Java?
Solution
-
le champ n’est pas privé
-
soit
Cla classe contenant le champ (la super-classe):-
le champ n’est pas référencé par les méthodes de
C -
le champ n’est pas référencée sur les instances de
C
-
2. Classes immuables
Dans cet exercice, vous allez implémenter, en Java, une collection générique, contenant seulement 3 méthodes : ajouter un élément, supprimer un élément et calculer la taille de la collection.
Cette classe doit répondre à deux exigences :
-
Elle doit être immuable, ses instances ne sont pas modifiables.
-
Elle ne doit pas accepter les valeurs nulles.
2.1. Signature
Commencez par spécifier seulement la signature des 3 méthodes.
Solution
-
Les méthodes
add()etremove()ne doivent pas retournervoid
ImmutableCollection<T> add(T element);
ImmutableCollection<T> remove(T element);
int size();
}
2.2. La classe ImmutableCollection
Donnez maintenant le code source de toute la classe, à l’exclusion des 3 méthodes, que vous spécifierez dans la suite de l’exercice.
Solution
-
Les classes immuables doivent être déclarées
final -
Ici, la solution utilise un tableau (
T[]), mais l’utilisation des classes de la JFC était possible -
La méthode
contains()n’est pas obligatoire
public final class ImmutableCollection<T> {
private final T[] elements;
public ImmutableCollection() {
elements = (T[]) new Object[0];
}
private ImmutableCollection(T[] elements) {
this.elements = elements;
}
private boolean contains(T element) {
for (int i = 0; i < elements.length; i++) {
if (elements[i].equals(element)) {
return true;
}
}
return false;
}
}
2.3. Ajout d’un élément
Donnez le code source de la méthode d’ajout d’un élément.
Solution
public ImmutableCollection<T> add(@Nonnull T element) {
Preconditions.checkNotNull(element);
if (this.contains(element)) {return this;}
Object[] newElements = new Object[elements.length + 1];
System.arraycopy(elements, 0, newElements, 0, elements.length);
newElements[elements.length] = element;
return new ImmutableCollection<>((T[]) newElements);
}
2.4. Suppression d’un élément
Donnez le code source de la méthode de suppression d’un élément.
Solution
public ImmutableCollection<T> remove(T element) {
Preconditions.checkNotNull(element);
if (!this.contains(element)) {return this;}
Object[] newElements = new Object[elements.length - 1];
int j = 0;
for (int i = 0; i < elements.length - 1; i++) {
if (!elements[i].equals(element)) {
newElements[j++] = elements[i];
}
}
return new ImmutableCollection<>((T[]) newElements);
}
3. Association Bidirectionnelle
On souhaite implémenter une association bidirectionnelle entre les
classes Task et Event. Une instance de la classe Task peut être
liée à au plus une instance de la classe Event et une instance de la
classe Event peut être liée à au plus une instance de la classe
Task.
Ces deux classes ont été codées partiellement en Java, comme suit:
public class Task {
private Optional<Event> event = Optional.empty();
public Optional<Event> getEvent() {
return this.event;
}
public void setEvent(Event event) {
this.unsetEvent();
event.unsetTask();
event.basicSetTask(this);
this.basicSetEvent(event);
}
public void basicUnsetEvent() {
this.event = Optional.empty();
}
public void basicSetEvent(Event event) {
this.event = Optional.ofNullable(event);
}
public void unsetEvent() {
event.ifPresent(e -> e.basicUnsetTask());
this.basicUnsetEvent();
}
}
public class Event {
private Optional<Task> task = Optional.empty();
public Optional<Task> getTask() {
return task;
}
public void setTask(Task task) {
}
public void unsetTask() {
task.ifPresent(t -> t.basicUnsetEvent());
this.basicUnsetTask();
}
public void basicSetTask(Task task) {
this.task = Optional.of(task);
}
public void basicUnsetTask() {
this.task = Optional.empty();
}
}
4. Le patrons de conception Singleton
4.1. Définition
Quel est l’objectif principal du patron de conception Singleton ? Dans quels cas est-il approprié de l’utiliser ?
Solution
-
L’objectif de ce patron est d’assurer qu’une classe n’a qu’une seule instance et de fournir un point d’accès global à cette instance.
-
L’objectif du patron singleton est d’assurer qu’une classe n’a qu’une seule instance, de fournir un point d’accès global à cette instance et de permettre sont instanciation de manière paresseuse.
-
On l’utilise lorsqu’il ne doit y avoir qu’une seule instance d’une classe, et elle doit être accessible aux clients à partir d’un point d’accès bien connu et lorsque cette unique instance doit être extensible par sous-classe et que les clients doivent être en mesure d’utiliser une instance d’une sous-classe sans modifier leur code
4.2. Mise en oeuvre
Implémentez, en Java, une classe appelée
System, en assurant que cette classe ne peut avoir qu’une seule instance, grâce au patron Singleton
Solution
public class System {
private System() {}
public static System getInstance() {
return Holder.instance;
}
private static class Holder {
public static final System instance = new System();
}
}