Cours Spé Dev - Design Pattern

Vidéo

Patrons de conception

Présentation

Dans toutes les grosses applications, les développeurs rencontrent différents problèmes de conception. La plupart de ces problèmes se retrouvent très régulièrement dans différents projets. Pour éviter de rencontrer ces problèmes à nouveaux, les développeurs ont mis au points les Design Patterns (ou patrons de conceptions en français).

Un design pattern a pour but de répondre à un problème précis. Dans ce cours, nous allons en voir quelques uns, mais sachez qu’il en existe énormément. Il n’est pas nécessaire de tous les connaître par cœur, mais de comprendre au moins leur fonctionnement et surtout leur intérêt.

Single Responsibility Principle

En français : le principe de responsabilité unique.

Une classe (et cela s’applique aussi aux fonctions), ne doit posséder qu’une et une seule responsabilité.

Si une classe gère plusieurs choses à la fois, alors elle va probablement être liées à d’autres classes. Du coup, les modifications apportées à une responsabilité vont forcément impacter une autre classe. Ce qui risque de fragiliser votre application.

Open Closed Principle

En français : le principe Ouvert Fermé

Une application doit être ouverte à l’extension et fermée aux modifications. C’est à dire que vous devez pouvoir ajouter de nouvelles classes/fonctionnalités, sans avoir à modifier tout votre code.

Problème 1

Gestion d’évènements

EventType e = data.ChoiceEvent();
switch ( e ) {
    case EventType.Trap:
        Console.WriteLine("Bla bla");
        hero.Health -= _random.Next(10, 30);
        // ...
        break;
    case EventType.HealFontain:
        Console.WriteLine("Bla bla");
        hero.Health += _random.Next(10,30);
        // ...
        break;
    // Autres cases
}

Solution : Strategy

Pattern Strategy
Pattern Strategy

Application (1)

EventStrategy

abstract class EventStrategy {
    abstract void execute(Hero hero, GameData data);
}

TrapEvent

class TrapEvent : EventStrategy {
    override void execute(Hero hero, GameData data) {
        Console.WriteLine("Bla bla");
        hero.Health -= data.Random.Next(10, 30);
    }
}

Application (2)

Gestion d’évènements

EventStrategy e = data.ChoiceEvent();
e.execute(hero, data);

Problème 2

Le TP casse-brique du premier semestre.

Pour rappel, vous aviez 5 briques qui rapportent des points et qui ont des effets différents. On vous avez demandé que chacune de ces briques pouvait exister en version “spéciale” qui double l’effet et le nombre de points.

Une mauvaise solution : créer 5 nouvelles briques qui héritent chacune de sa brique de base, et appliquent 2 fois l’effet.

Une bonne solution : créer une seule nouvelle brique qui fonctionne pour toutes les briques. C’est le décorateur.

Solution : Décorateur

Pattern Decorator
Pattern Decorator

Application (1)

Brique

class Brique {
    virtual int points() {
        return 1;
    }
    virtual void effet(Raquette r) {
    }
}

BriqueJaune

class BriqueJaune : Brique {
    override int points() {
        return base() * Random.Next(1,5);
    }
}

Application (2)

DecorateurBrique

class DecorateurBrique : Brique {
    Brique brique;
    override int points() {
        return brique.points() * 2;
    }
    override void effet(Raquette r) {
        brique.effet(r);
        brique.effet(r);
    }
}

Problème 3

Equipement

class Equipement {
    // Attributs
    void Equiper(Hero hero) {
        Console.WriteLine("Vous avez choisi de vous equiper 
        de " + name);
        if ( isWeapon ) {
            hero.strength += stat;
        } else {
            hero.defense += stat;
        }
        Console.WriteLine("Vos nouvelles statistiques : " + 
        hero);
    }
}

Solution : Template Method

Template methdo
Template methode

Application (1)

Equipement

class Equipement {
    // Attributs
    void Equiper(Hero hero) {
        Console.WriteLine("Vous avez choisi de vous equiper 
        de " + name);
        effet(hero);
        Console.WriteLine("Vos nouvelles statistiques : " + 
        hero);
    }
    
    abstract void effet(Hero hero);
}

Application (2)

Weapon

class Weapon : Equipement {
    override void effet(Hero hero) {
        hero.strength += stat;
    }
}

Armor

class Armor : Equipement {
    override void effet(Hero hero) {
        hero.Defense += stat;
    }
}

Problème 4

Vous souhaitez n’avoir qu’une seule instance d’un objet pour tout votre programme. Par exemple : vous ne voulez qu’une seule instance d’un système de sauvegarde.

Vous voulez n’avoir qu’un seul objet Sauvegarde pour tout le problème, il n’y a aucune raison d’avoir plusieurs objets Sauvegarde.

Savestate

class Savestate {
    void save() {
        // code
    }
    void load() {
        // code
    }
}

Solution : Singleton

Template methdo
Template methdo

Application

Savestate

class Savestate {
    private static Savestate instance;
    private Savestate() {}
    public static Savestate Instance {
        get {
            if ( instance == null)
                instance = new Savestate();
            return instance;
        }
    }
    // code précédent
}