On poursuit le développement de l'application permettant le traitement de virements entre entités capables de les émettre et/ou de les recevoir commencé dans le TP 1. # Modification de l'état d'un record Nous avons vu que l'intention pour les types `{java}record` était de définir des types immuables. Sous l'hypothèse que vous ayez représenté les **virements** sous forme de `{java}record`, on pourrait faire les opérations ci-dessous : ```java Virement v = new Virement(TypeVirement.REMBOURSEMENT, new Personne("Bobby", "Joe", "bobjo", 14), new Personne("Anne", "Atol", "atol", 5000), 55, "ce que tu m'as avancé au restaurant l'autre jour"); System.err.println("\n\n" + v); EmetteurVirement ev = v.émetteur(); System.err.println("émetteur virement : " + ev); if (ev instanceof Personne evp) { evp.ajouteSolde(1000); System.err.println("personne émettrice + 1000 : " + evp); } ``` >[!Question] Qu'observe-t-on dans cet exemple ? Qu'est-ce que cela dit de l'_immuabilité_ des objets de type `{java}Virement`? Comment qualifieriez-vous une telle immuabilité ? >votre réponse ici ^TP2QuestionImmuabiliteRecordNEPASEDITER >[!Question] Qu'imposerait une immuabilité plus stricte ? >votre réponse ici ^TP2QuestionImmuabiliteStricteRecordNEPASEDITER Adaptez votre implémentation d'un `{java}record` `{java}Virement` pour que, en particulier, l'**émetteur** d'un **virement** ne puisse plus subir d'altération comme dans l'exemple ci-dessus (ne pas vous intéresser ici aux autres composantes du `{java}record`). ```java title:"votre code pour la non altération des composants d'un record" // votre code ici ``` ^TP2NonAlterationComposantRecordNEPASEDITER # Création de virements par _wither_ Les **virements** doivent être immuables, mais il pourrait être intéressant d'obtenir des variantes (elles-mêmes immuables) de virements pour lesquels on aurait changé une ou plusieurs valeurs de composantes (par exemple, changer uniquement le montant d'un précédent **virement** en conservant les autres composantes). Appliquez l'approche des _withers_ dans le type `{java}Virement` et testez-la. ```java title:"votre code pour la construction par withers d'instances de Virement" // votre code ici ``` ^TP2WitherVirementNEPASEDITER # Création de personnes par _builder_ On cherche à proposer une autre manière de créer des instances de notre type `{java}Personne` en suivant l'approche dite _builder_, ce qui permettrait des constructions telles que : ```java Personne p = Personne.builder() .nom("joe") .prénom("bobby") .pseudo("alf") .build(); ``` Implémentez cette approche, en vous assurant que la méthode `{java}build` refuserait la création d'instances non valides (par exemple en propageant une exception de type `{java}IllegalArgumentException`), et testez-la. ```java title:"votre code pour la construction par builder d'instances de Personne" // votre code ici ``` ^TP2BuilderPersonneNEPASEDITER >[!Question] Dans quelles situations le recours à une telle approche vous semblerait inadapté ? (voir des pistes [ici](https://dev.to/siy/when-builder-is-anti-pattern-3j92) ou [là](https://medium.com/javarevisited/misuse-of-builder-pattern-bfbdd9061288)) >votre réponse ici ^TP2QuestionBuilderPersonneNEPASEDITER >[!Question] Que permet la librairie [RecordBuilder](https://github.com/randgalt/record-builder) ? Pour quelles raisons a-t-on développé ce type de librairie ? >votre réponse ici ^TP2QuestionRecordBuilderNEPASEDITER # Représentation de la banque centrale On souhaite ajouter le code nécessaire pour représenter une **banque centrale**, chargée de percevoir provisoirement le montant d'un **virement** avant que que le **résultat du virement** ne soit reçu, et que le montant soit transféré au **récepteur** du virement (_virement accepté_), ou restitué à l'**émetteur** du virement (_virement refusé_). Pour cela, votre application devra autoriser à l'exécution une et une seule instance de cette classe. Une approche possible reposerait sur la propriété qu'ont les **constantes d'énumération** de correspondre à des instances uniques. Un type tel que : ```java public enum MaClasseAInstanceUnique { INSTANCE; public boolean uneMéthode() { return true; } // ... } ``` n'autoriserait donc que la valeur `{java}MaClasseAInstanceUnique.INSTANCE`, et permettrait des appels tels que `{java}MaClasseAInstanceUnique.INSTANCE.uneMéthode()`. On peut toutefois penser que cette écriture ne faciliterait pas la compréhension. Une alternative serait la suivante : fournir dans la classe une méthode `{java}static` permettant d'obtenir une référence sur l'unique instance (genre, `{java}public static MaClasseAInstanceUnique getInstance()`), qui ne serait pas directement constructible depuis l'extérieur de la classe par appel d'un constructeur (celui-ci serait donc `{java}private`), mais serait donc initialisée de façon statique (par exemple dans un bloc d'initialisation `{java}static`). Fournissez ci-dessous le code ajouté / modifié pour représenter la **banque centrale**. ```java title:"votre code" ``` ^TP2BanqueCentraleNEPASEDITER > [!Question] Cette approche correspond en fait à un patron de conception bien identifié : comment le nommeriez-vous ? Dans quelles situations serait-il utile ? >votre réponse ici ^TP2QuestionPatronBanqueCentraleNEPASEDITER > [!Question] En quoi cette approche est-elle différente de celle qui aurait consisté à simplement avoir un champ statique pour la banque centrale dans votre application ? >votre réponse ici ^TP2QuestionChamptStaticBanqueCentraleNEPASEDITER # Protection par utilisation de _valeurs optionnelles_ Dans certains cas, des méthodes avec un type de retour peuvent ne pas pouvoir renvoyer de résultat. Cela est souvent indiqué à l'aide de la valeur particulière `{java}null` pour les références d'objet. > [!Question] Pourquoi est-ce que cette approche présente-t-elle des risques ? > votre réponse ici ^TP2QuestionNullMethodNEPASEDITER > [!Question] Que permet la proposition [JEP 358: Helpful NullPointerExceptions](https://openjdk.org/jeps/358) ? Cela résoud-il ou minimise-t-il le problème précédent ? > votre réponse ici ^TP2QuestionHelfulNPENEPASEDITER Java 8 a introduit dans le langage la notion de _valeur optionnelle_ à travers la classe paramétrée [`{java}java.util.Optional`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) : les instances sont des enveloppes qui peuvent contenir ou non une référence d'objet. Ce type propose donc une méthode [`{java}isPresent()`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#isPresent--) qui renseigne sur la présence d'une valeur ou non, la méthode [`{java}get`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#get--) permettant alors de récupérer la valeur effectivement contenue (voir également la méthode [`{java}orElse`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#orElse-T-)). La valeur "_absence de valeur_" s'obtient à l'aide de la méthode [`{java}empty`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#empty--), et une instance peut être créée à partir de références à l'aide des méthodes [`{java}of`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#of-T-) et [`{java}ofNullable`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#ofNullable-T-). Réécrivez vos méthodes surchargées précédentes `{java}cléAléatoireDUnEnsemble` de sorte à ce qu'elles retournent désormais le type `{java}Optional`, et illustrez leur utilisation à l'aide d'exemples. ```java title:"votre code modifié : méthodes et utilisation" ``` ^TP2OptionalKeyOfSetNEPASEDITER > [!Question] Dans ce cas particulier, quels bénéfices voyez-vous au recours à cette classe `{java}Optional` ? Voyez-vous d'autres cas d'usages qui pourraient être intéressants ? >votre réponse ici ^TP2QuestionOptionalNEPASEDITER # Ouverture à l'ouverture des classes scellées Supposons que vous souhaitiez permettre l'héritage à partir de votre classe`{java}Personne` pour permettre deux uniques classes filles, `{java}PersonneAvare` et `{java}PersonneGénéreuse`. Un type scellé semble pertinent. La [documentation du langage](https://docs.oracle.com/en/java/javase/17/language/sealed-classes-and-interfaces.html) nous apprend que toute sous-classe d'une classe scellée doit définir un modificateur parmi `{java}final`, `{java}sealed` et `{java}non-sealed`. > [!Question] La tentative ci-dessous, qui introduirait une classe `{java}PersonneTrèsGénéreuse`, est-elle possible ? Pourquoi, et éventuellement en quoi cela contournerait, ou non, les protections permises par les classes scellées ? > votre réponse ici ^TP2QuestionSealedSubclassNEPASEDITER ```java sealed class Personne permits PersonneAvare, PersonneGénéreuse [implements ...] { // ... } final class PersonneAvare extends Personne { // ... } non-sealed class PersonneGénéreuse extends Personne { // ... } final class PersonneTrèsGénéreuse extends PersonneGénéreuse { // ... } ``` > [!Question] 🤔Mais pourquoi est-ce que dans l'exemple vu en cours (recopié ci-dessous) aucun mot-clé parmi `{java}final`, `{java}sealed`, et `{java}non-sealed` n'apparaît pour `{java}AchatPrixMarché` et `{java}AchatPrixLimité` ? > votre réponse ici ^TP2QuestionSealedRecordNEPASEDITER ```java public sealed interface Achat permits AchatPrixMarché, AchatPrixLimité { int nombre(); PairDevises paire(); LocalDateTime dateOrdre(); } public record AchatPrixMarché(int nombre, PaireDevises paire, LocalDateTime dateOrdre) implements Achat { ... } public record AchatPrixLimité(int nombre, PaireDevises paire, LocalDateTime dateOrdre, double prix) implements Achat { ... } ``` # Modification de champs par introspection L'[API d'introspection](https://dev.java/learn/reflection/) (_reflection_) du langage permet des opérations à l'exécution très particulières sur les types et les objets. Comme le montre l'exemple ci-dessous, appliqué à une instance d'une classe `{java}Personne`, elle permet par exemple d'obtenir l'ensemble des champs d'une classe, d'obtenir un accès sur un champ particulier ❶, de modifier dynamiquement l'accessibilité d'un tel champ ❷ puis de l'altérer ❸. ```java try { Personne p = new Personne("Al", "Bator", "bibiphoque", 40); System.out.println("solde (via getter) = " + p.getSolde()); java.lang.reflect.Field champSolde = p.getClass().getDeclaredField("solde"); // ❶ champSolde.setAccessible(true); // ❷ 😨 int solde = (int) champSolde.get(p); System.out.println("solde (via introspection) = " + solde); champSolde.setInt(p, 5600); // ❸ 😱 solde = (int) champSolde.get(p); System.out.println("solde (via introspection) = " + solde); System.out.println("solde (via getter) = " + p.getSolde()); // 🤯 } catch (NoSuchFieldException nsfe) { System.err.println("no such field exception : " + nsfe.getMessage()); } catch (IllegalAccessException iae) { System.err.println("illegal access exception : " + iae.getMessage()); } ``` Essayez un tel code, en l'adaptant à votre propre classe `{java}Personne` (on fait l'hypothèse raisonnable qu'un champ tel que `{java}solde` existe, qu'il est `{java}private` mais non `{java}final`). > [!Question] Êtes-vous horrifié·e par ce que vous venez de voir ? Comment et pourquoi un tel mécanisme peut-il être accessible ? >votre réponse ici ^TP2QuestionIntrospectionClasseNEPASEDITER Essayez à présent la même chose sur votre classe `{java}Virement` (on fait l'hypothèse qu'elle est de type `{java}record`), en altérant cette fois le **montant** d'un **virement**. > [!Question] Que constatez-vous à présent ? Pourquoi ? (voir [cette section pour des indices](https://dev.java/learn/reflection/records/#fields)) >votre réponse ici ^TP2QuestionIntrospectionRecordNEPASEDITER