A31 - Conception et programmation objet avancées

1 - Principes et patrons de conception

Adrien Krähenbühl IUT Robert Schuman

Déroulement du module

Présentations

Équipe pédagogique

Adrien Krähenbühl
CMx + Groupe 34 de TDs + Groupe 3 de TPs
Ludovic Christmann
Groupe TDs/TPs 12
Romain Orhand
Groupe TP 4
Romain Perrin
Groupe TDs/TPs 56

Séances

  • 3 CMx \(\rightarrow\) 1 \(\times\) 2h + (2 ou 3) \(\times\) 1h
  • 6 TDs
  • 7 TPs classiques jusqu’octobre
  • 7 TPs pour réaliser un projet sur novembre/décembre

Détail des séances

Contenu

  • Principes de conception
  • Patrons du “Gang of Four”
  • GRASP et SOLID
  • Swing

Note : Le contenu est construit sur la base du PPN 2013 (page 46).

Répartition

1\(^{ère}\) séquence : CM 2h + 2 TDs + 3 TPs
2\(^{ème}\) séquence : CM 1h + 2 TDs + 2 TPs
3\(^{ème}\) séquence : CM 1h + 2 TDs + 2 TPs
4\(^{ème}\) séquence : TP x 7

Évaluation du module

Trois notes

1 QCM
Avant les vacances de la Toussaint.
1 TP noté
1\(^{ère}\) semaine de novembre.
1 projet en binôme
7 séances de TP dédiées après les vacances de la Toussaint.

Calcul de votre moyenne

Note finale = (0.25 \(\times\) QCM) + (0.35 \(\times\) TP noté) + (0.4 \(\times\) projet)

Introduction

Qu’est-ce qu’un patron de conception ?

Un patron de conception (design pattern) est une solution éprouvée à un problème concret et récurrent de programmation.

Les patrons :

  • sont représentés par des diagrammes de classes
  • évitent de ré-inventer le roue

… mais :

  • doivent être utilisés dans des cas bien identifiés
  • ne sont pas la solution à tous les problèmes

Qu’est-ce qu’un principe de conception ?

Un principe de conception est un principe général permettant de prendre de bonnes décisions, qui n’est pas spécifique à un problème.

« Le meilleur outil de conception pour le développement de logiciels est un esprit bien éduqué sur les principes de conception. Ce n’est pas UML ou toute autre technologie. » Craig Larman, 2005.

Il en existe beaucoup :

GRASP - SOLID - KISS - DRY - YAGNI - La loi de Demeter - …

Attention, un principe N’EST PAS un patron.

Historique



Origine en architecture en 1977
“A pattern language: Towns, Buildings, Construction”
C. Alexander et al.
Récupération en IHM (Interaction design) en 1986…
“User Centered System Design”, D. Norman & S. Draper
… puis en informatique
  • 1987 : 1er projet de conception mettant en oeuvre des patrons de conception par K. Beck & W. Cunningham chez Tektronix
  • 1991 : “Design Patterns: Elements of Reusable Object-Oriented Software” par le Gang Of Four : E. Gamma, R. Helm, R. Johnson & J. Vlissides
                          \(\rightarrow\) Référence de la COO

Les principes de conception

Le principe KISS

KISS (Keep It Simple, Stupid) signifie “Restez simple, stupide”.

Principe :

  • Toujours choisir la simplicité
  • Éviter autant que possible la complexité

En POO, cela consiste notamment à privilégier plusieurs classes ou fonctions simples à une classe ou fonction complexe.

Le principe DRY

DRY (Don’t Repeat Yourself) signifie “Ne vous répétez pas”.

Principe :

  • La duplication c’est le mal
  • Mutualisez ce qui est mutualisable

public int somme( int a, int b ) {
    return a+b;
}
public double somme( double a, double b ) {
    return a+b;
}

\(\rightarrow\)

public <T> T somme( T a, T b ) {
    return a+b;
}

La généricité est un exemple d’application du principe DRY.

Le principe YAGNI

YAGNI (You ain’t gonna need it) signifie
“Vous n’en aurez pas besoin”.

Ne codez que des choses utiles maintenant, le reste est une perte de temps qui peut être source de bugs.

public class MaClasse {
    private Integer _value;
    // Constructeur inutile
    public MaClasse() {
        _value = 3;
    }
    public MaClasse( Integer value ) {
        _value = value;
    }
}

\(\rightarrow\)

public class MonAppli {
    public static void main( String args ) {
        MaClasse obj = new MaClasse(5);
    }
}

La “Loi de Demeter”

La loi de Demeter peut se résumer par
“Ne communiquez qu’avec vos voisins immédiats”.

public class Belote {
    private List<Player> _players;
    public void nextRound() {
        for ( Player player : players ) {
            Integer randomIndex = ...
            Card cardToPlay =
                player.getCards().remove(randomIndex);
            ...
        }
    }
}
public class Player {
    private List<Card> cards;
    public List<Card> getCards() { return cards; }
}
public class Card { ... }

\(\rightarrow\)

public class Belote {
    private List<Player> players;
    public void nextRound() {
        for ( Player player : players ) {
            Card cardToPlay = player.takeNextCard();
            ...
        }
    }
}
public class Player {
    private List<Card> cards;
    public Card takeNextCard() {
        Integer randomIndex = ...
        Card cardToPlay = cards.remove(randomIndex);
    }
}
public class Card { ... }

… et les “groupes” de principes

Ils ont été présentés en A22, il s’agit de :

GRASP
  • proposés par Craig Larman
  • gestion de l’assignation des responsabilités

et :

SOLID
  • proposés par Robert C. Martin
  • couvrent l’assignation des responsabilités et l’héritage


Nous y reviendrons plus tard.

Les patrons du GoF

Objectifs et description

Rappel : Un patron de conception est une solution éprouvée à un problème concret et récurrent de programmation.

Objectifs
  • L’objectif est d’obtenir un code MAINTENABLE et RÉUTILISABLE
  • Les design pattern résultent de l’expérience de nombreux développeurs
Fiche de description
  • un nom
  • une description du problème à résoudre
  • une description de la solution \(\rightarrow\) le DP
  • les conséquences et/ou implications issus de la solution

Trois types de patrons

Les patrons de création
décrivent comment instancier les classes, c’est-à-dire comment créer et configurer des objets.
Les patrons de structure
décrivent comment structurer les classes afin d’avoir un minimum de dépendances entre l’implémentation et l’utilisation.
Les patrons de comportement
décrivent une structure de classes permettant de faire collaborer des objets efficacement.

Les 23 patrons

Quelques
patrons de création

1. Singleton

Problème
Comment permettre à une classe de n’avoir qu’une et une seule instance quelle que soit son utilisation ?

Solution

La classe interdit aux autres classes de la construire et gère elle-même son unique instance.

Utilisation (java)

Singleton instance = new Singleton(); // ERREUR DE COMPILATION
Singleton uniqueInstance = Singleton.getInstance(); // OK

2. Factory method - le problème

Problème
Plusieurs classes concrètes héritent de la même classe abstraite. Comment faire pour qu’une classe externe puisse instancier des objets de chacune des classes concrètes sans dépendance ?

2. Factory method - la solution

Solution
Créer une classe dédiée à la fabrication des classes concrètes.
Conséquences
  • Une classe externe ne dépendra que de la classe abstraite et de la fabrique quel que soit le nombre de classes concrètes.
  • Ajouter une nouvelle classe concrète implique de créer une nouvelle fonction dans la fabrique.

Quelques
patrons de comportement

1. Template method - le problème

Problème
Comment mutualiser du code partiellement redondant lorsqu’une structure algorithmique similaire se dégage ?

1. Template method - La solution

AlgoTemplate basic = new BasicAlgo();
basic.execute(); // convertFromArray + exchangeSort
AlgoTemplate funny = new FunyAlgo();
funny.execute(); // convertFromGraph + bubbleSort
Solution
Créer une classe avec une méthode qui réalise l’algorithme commun en appellant des méthodes abstraites redéfinies dans ses sous-classes.

1. Template method - Les conséquences



  • Modifier l’algorithme commun ne nécessite que de modifier la classe Template.
  • Ajouter une nouvelle variante ne nécessite que de coder les méthodes abstraites de la classe Template.
  • Une méthode identiques à plusieurs variantes peut devenir une méthode concrète de la classe Template, redéfinie dans les variantes où elle diffère (loadElements dans l’exemple).

2. Strategy - le problème

Problème
Comment permettre à un client de choisir dynamiquement le comportement d’une méthode selon un contexte donné ?

2. Strategy - la solution


Solution

Définir un contexte :

  • qui possède une stratégie modifiable,
  • qui utilise la dernière stratégie sélectionnée lorsqu’on l’utilise.

2. Strategy - les conséquences

Conséquences
  • Le choix de la stratégie et son exécution son découplées
  • Pas besoin de modifier le contexte lorsqu’on ajoute une nouvelle stratégie
  • L’ajout d’une nouvelle stratégie n’implique aucune modification de son exécution

Exemple avec les stratégies de tri

3. Observer - le problème

Problème
Comment permettre à des objets de réagir au comportement d’autres objets tout en limitant les dépendances et la redondance du code ?

3. Observer - la solution

AnObjectObserver observerOne = new ReactiveObject();
AnObjectObserver observerTwo = new AnotherReactiveObject();
AnObject anObject = new AnObject();
anObject.addObserver(observerOne);
anObject.addObserver(observerTwo);
anObject.doSomething();
Solution
  • Créer une interface pour les observateurs
  • Faire gérer une liste d’observateurs par la classe observable
  • Notifier tous les observateurs lorsque nécessaire

3. Observer - les conséquences

  • Il est possible d’ajouter (ou supprimer) dynamiquement des observateur à l’objet observable
  • La méthode notifyObservers() peut être appelée à n’importe quel moment de n’importe quelle méthode de la classe observable.
  • Une classe qui souhaite observer les objets observables doit simplement implémenter l’interface Observer. Le code de l’objet observable ne sera pas modifié.

Quelques liens utiles


À lire avant toute chose
C’est quoi être professionnel quand on est développeur ?


Patrons du GoF