image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

Tirelire scellée

Depuis Java 17 (Java 15 en preview), nous disposons du mécanisme de classes scellées afin de contrôler la descendance d'une classe.

Faîtes les modifications nécessaires (quelques mots-clés suffisent) afin de n'autoriser comme descendance de MoneyBox que SimpleMoneyBox et EnhancedMoneyBox.

Si vous utilisez une classe abstraite AbstractMoneyBox, vous pouvez autoriser uniquement AbstractMoneyBox pour hériter de MoneyBox et n'autoriser comme classes dérivées de AbstractMoneyBox que SimpleMoneyBox et EnhancedMoneyBox.

On fera en sorte d'interdire toute classe dérivée de SimpleMoneyBox ; par contre il sera possible d'étendre EnhancedMoneyBox.

Fabricant de tirelire

Nous souhaitons maintenant écrire une classe permettant de fabriquer une tirelire. Nous nommerons cette classe MoneyBoxBuilder.

Elle dispose d'un setter permettant d'indiquer si l'on souhaite construire une tirelire simple ou avancée : void setEnhanced(boolean value). Par défaut enhanced est fixé à false (construction de SimpleMoneyBox).

Le fabricant de tirelire, dans sa grande générosité, insère une certain nombre de pièces aléatoires au moment de la fabrication de la tirelire. On peut fixer le nombre de pièces insérées avec void setInitialCoinNumber(int coinNumber).

D'autre part, on peut définir la capacité initiale de la tirelire avec void setCapacity(int capacity)

Changez le type de retour des méthodes précédentes de void à MoneyBoxBuilder afin de retourner this. Vous pourrez ainsi chaîner les méthodes :

var builder = new MoneyBoxBuilder().setEnhanced(true).setCapacity(100).setInitialCoinNumber(2);

Rajoutez une méthode MoneyBox build() construisant la tirelire avec le paramétrage souhaité.

Egalité de tirelire

Nos tirelires disposent d'une fonctionnalité magique. Bien que nous ne puissions pas connaître leur contenu avant de les casser, il est possible de savoir si deux tirelires sont égales ou non.

Pour que deux tirelires soient égales, il faut :

Nous vous demanderons donc tout d'abord de redéfinir la méthode boolean equals(Object obj) du type ancêtre de SimpleMoneyBox et EnhancedMoneyBox. Cette méthode devra tester l'égalité de classe, de capacité, de montant et d'état de casse.

Avez-vous besoin ensuite de redéfinir equals pour SimpleMoneyBox ? Pour EnhancedMoneyBox ? Faîtes les ajouts qui vous paraissent nécessaires.

Avez-vous besoin aussi de redéfinir int hashCode() ? Quelles conséquences aurait la non-redéfinition de hashCode alors que equals a été redéfinie ?

Cloneur de tirelire

Pour finir, nous souhaitons mettre au point une invention exclusive : le cloneur de tirelire (MoneyBoxCloner).

Un cloneur de tirelire dispose d'une unique méthode MoneyBox clone(MoneyBox mb) capable de prendre en paramètre une MoneyBox et retournant un nouvel objet MoneyBox similaire en tout point à la tirelire originale.

Pour cela, nous devons tester le type de la tirelire (SimpleMoneyBox ou EnhancedMoneyBox) afin de déterminer la tirelire à construire. On insérera ensuite les pièces nécessaires pour obtenir le même état et l'on cassera la tirelire si nécessaire.

On notera que l'on aura besoin d'accéder au montant de la tirelire même si celle-ci n'est pas cassée. Pour cela, on pourra écrire une méthode spécifique permettant d'obtenir le montant total quel que soit l'état de la tirelire mais dont l'accessibilité sera limitée au seul paquetage (et on s'assurera que MoneyBoxCloner est dans le même paquetage pour pouvoir y accéder).

Nous aurons besoin d'utiliser des instanceof pour tester le type de la tirelire à cloner ; on pourra aussi utiliser un switch-case judicieux (attention, cette dernière fonctionnalité n'est accessible que depuis Java 18).

Paradoxalement, le clonage d'une SimpleMoneyBox est un peu plus difficile qu'une EnhancedMoneyBox. Il faut en effet pouvoir déterminer une séquence de pièces à insérer permettant d'arriver au montant global de la tirelire à copier. On peut par exemple essayer d'insérer des pièces de valeur importante puis ensuite se replier sur les pièces de valeur plus faible lorsque l'on approche du montant cible.

Avant de retourner la tirelire clonée cloned, on pourra tester que celle-ci est identique à la tirelire originale original avec l'assertion :

assert(cloned.equals(original))

Nous remarquons que nous n'avons besoin que d'une seule instance de MoneyBoxCloner (en effet MoneyBoxCloner ne conserve pas d'état). Quel patron de conception pourrait-on mettre en œuvre ?