Contrôle continu 2025-26

1. Techniques de codage

L’objectif de cet exercice est d’implémenter la classe ImmutableRGB, qui représente l’encodage de couleur "rouge-vert-bleu". Dans cet encodage, chacune des couleurs peut avoir une valeur entre 0 et 255. La spécification de cette classe est donnée par le diagramme de classes UML présenté dans la figure ci-dessous.

Pour l’implémenter, vous devez respecter les règles suivantes :

  1. La classe est immuable ;

  2. Les valeurs nulles ne sont pas acceptées ;

  3. La classe doit redéfinir la méthode Object::equals(Object) ;

La classe Immutable RGB
Figure 1. La classe Immutable RGB

1.1. Signature de la classe

Commencez par déclarer la signature de la classe, sans spécifier son corps (son contenu) :

Solution
public final class ImmutableRGB {} (1)
  1. Le mot-clé final est obligatoire ici.

1.2. Champs

Déclarez maintenant les champs de la classe :

Solution
final private short red;
final private short green;
final private short blue;
final private String name;
  1. Tous les attributs doivent être déclarés final

1.3. Constructeur

Déclarez le constructeur de la classe :

Solution
public ImmutableRGB(short red, short green, short blue, @NotNull String name) {
    if (name == null || red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) {
        throw new IllegalArgumentException();
    }
    this.red = red;
    this.green = green;
    this.blue = blue;
    this.name = name;
}
  1. En Java, les types primitifs ne peuvent pas avoir une valeur nulle ;

  2. L’utilisation d’une garde est nécessaire.

1.4. La méthode invert()

Déclarez la méthode invert(). Cette méthode rend un nouvel encodage, qui correspond à l’inverse de l’objet. L’inverse d’une couleur correspond à son complément à 255. Par exemple, l’inverse de l’encodage (r=0,b=255,g=100) est (r=255,b=0,g=155)

Solution
public ImmutableRGB invert() {
    return new ImmutableRGB((short) (255 - red), (short) (255 - green), (short) (255 - blue), "Inverse of " + name);
}
  1. L’argument name peut avoir n’importe quelle valeur, à part la valeur nulle

  2. La création d’une nouvelle instance est obligatoire.

1.5. Égalité

Déclarez la méthode equals() :

Solution
public boolean equals(Object o) {
    if (!(o instanceof ImmutableRGB that)) return false;
    return red == that.red && green == that.green && blue == that.blue && name.equals(that.name);
}

1.6. Accesseurs

Déclarez le ou les accesseurs des champs :

Solution
public String getName() {
    return name;
}

1.7. Explications sur l’implémentation d’une classe immuable

Expliquez comment votre code assure que les objets de cette classe sont immuables :

Solution
Comme vu en TD :
  1. La classe est déclarée final : elle ne doit pas pouvoir être héritée.

  2. Les champs non transitoires sont déclarés private et final

1.8. Explication sur les champs

Expliquez comment votre code assure que les champs ne contiennent pas de valeurs nulles et comparez votre solution avec les alternatives vues en cours.

Solution
  1. Les types de base de Java, comme short ici, ne peuvent pas avoir une valeur nulle

  2. Le constructeur utilise une garde pour empêcher les instances de cette classe d’avoir des valeurs invalides.

Alternatives
  • Les annotations sont utiles pour trouver des erreurs pendant la compilation, mais ne peuvent pas empêcher des utilisations incorrectes.

  • Les assertions ne sont pas souhaitables ici, car il s’agit d’un constructeur public et que l’on ne peut pas connaître à l’avance la valeur des arguments passés au constructeur.

1.9. Méthode de test

Écrivez une méthode de test JUnit capable de tester la méthode ImmutableRGB::invert().

Solution
    @Test
    void invert() {
        ImmutableRGB actual = new ImmutableRGB((short) 0, (short)255, (short) 100, "actual");
        ImmutableRGB expected = new ImmutableRGB((short) 255, (short)0, (short)155, "Inverse of actual");

        assertEquals(expected, actual.invert());
    }
  1. Le nom attendu dépend de l’implémentation de la méthode invert()

2. Le patron Décorateur

Considérez la classe Character, qui représente un personnage de jeu de rôles et dont le code source Java est le suivant :

public class Character {
    private String name;
    private int    life;
    private Point  position;
    private int    currentHP;
    private int    intellect;
    private int    strength;

    public int attack() {
        return strength * life/100;
    }

    public int defend() {
        return intellect * life/100;
    }

    public void receiveHit(int intensity){
        life = life - intensity;
    }

    public boolean isAlive() {
        return life > 0;
    }

    public int getLife() {
        return life;
    }

    public String getName() {
        return name;
    }
}

On souhaite faire évoluer cette mise en œuvre pour permettre la gestion d’équipements, comme un bouclier ou une épée, qui modifient le comportement d’un personnage. Pour ce faire, nous allons utiliser le patron de conception Décorateur. Proposez une solution (une instance du patron Décorateur) permettant la gestion de deux équipements : les boucliers et les épées. Le bouclier doit multiplier par 3 la force de défense, c’est à dire, le résultat de la méthode defend(). L’épée doit multiplier par 5 la force d’attaque , c’est à dire, le résultat de la méthode attack(). Le bouclier et l’épée peuvent être utilisés par n’importe quel personnage.

2.1. Le composant

Tout d’abord, implémentez l’interface ou la classe abstraite jouant le rôle de Composant :

Solution
public interface Character {
    int attack();
    int defend();
    void receiveHit(int intensity);
    boolean isAlive();
    int getLife();
    String getName();
}

2.1.1. Le composant concret

Ensuite, modifiez la classe Character, pour la faire jouer le rôle de Composant Concret. Ne copiez pas le code de la classe, décrivez seulement les changements :

Solution
public class RealCharacter implements Character {
    // Le corps reste le même
}

2.2. Le décorateur Bouclier

Maintenant, implémentez la classe jouant le rôle de Décorateur pour le comportement d’un Bouclier :

Solution
public class ShieldDecorator implements Character {

    private final Character character;

    public ShieldDecorator(Character character) {
        this.character = character;
    }

    @Override
    public int defend() {
        return character.defend() * 3;
    }

    @Override
    public int attack() {
        return character.attack();
    }

    @Override
    public void receiveHit(int intensity) {
        character.receiveHit(intensity);
    }

    @Override
    public boolean isAlive() {
        return character.isAlive();
    }

    @Override
    public int getLife() {
        return character.getLife();
    }

    @Override
    public String getName() {
        return character.getName();
    }
}

2.3. Le décorateur Épée

Enfin, implémentez la classe jouant le rôle de Décorateur pour le comportement d’une Épée :

Solution
public class SwordDecorator implements Character {

    private final Character character;

    public SwordDecorator(Character character) {
        this.character = character;
    }

    @Override
    public int attack() {
        return character.attack() * 5;
    }

    @Override
    public int defend() {
        return character.defend();
    }

    @Override
    public void receiveHit(int intensity) {
        character.receiveHit(intensity);
    }

    @Override
    public boolean isAlive() {
        return character.isAlive();
    }

    @Override
    public int getLife() {
        return character.getLife();
    }

    @Override
    public String getName() {
        return character.getName();
    }
}