Méthodes pour factoriser du code - Héritage et agrégation avec délégation
Les exercices qui suivent concernent une architecture en couches et passent en revue différentes méthodes de factorisation du code pour de telles architectures :
- héritage avec approche ascendante,
- héritage avec approche descendante,
- héritage multiple (en Java 8 uniquement),
- agrégation avec délégation.
On cherche à programmer un agent cherchant à envoyer un message (sur un réseau) en utilisant un canal de communication, chargé de l'émission du message sur le réseau. Ce processus possède deux degrés de liberté, ou deux points de variation :
- la décomposition du message à envoyer en plusieurs fragments à émettre,
- le protocole de communication utilisé par le canal.
On suppose par la suite que deux techniques de décomposition sont utilisées :
- décomposition d'un message de taille quelconque en des fragments de taille limitée,
- pas de décomposition, le message étant simplement encapsulé pour le transport sur le réseau.
De même, on suppose que deux protocoles peuvent être utilisés, qu'on appellera "Protocole 1" et "Protocole 2" simplement. De ces deux hypothèses, il résulte qu'il est possible d'obtenir quatre combinaisons.
Découpage | Encapsulation | |
Protocole 1 | X | X |
Protocole 2 | X | X |
Quatre combinaisons d'implémentations
Dans la suite, on modélise la communication sur le réseau par une sortie sur la console, préfixée par le nom du protocole.
public void emettre(String msg) { System.out.println("protocole X : " + msg); }
On implémente ainsi l'encapsulation et le découpage des messages.
// Encapsulation public void envoyer(String msg) { this.emettre(msg); } // Découpage public void envoyer(String msg) { int TAILLE = 5; StringBuilder m = new StringBuilder(msg); int q = msg.length() / TAILLE; int r = msg.length() % TAILLE; for(int j = 0; j < q; j++){ this.emettre(m.substring(j * TAILLE, (j+1) * TAILLE)); } this.emettre(m.substring(q * TAILLE, q * TAILLE + r)); }
Pour chaque classe implémentée, on indique dans son nom les choix réalisés :
- DecoupantMessages et EncapsulantMessages pour le découpage et l'encapsulation respectivement,
- Protocole1 et Protocole2 pour les protocoles 1 et 2 respectivement.
Pour illustrer ce type d'architecture, prenons à titre d'exemple Internet. Les messages y sont décomposés en paquets dont la taille maximale est fixée par le MTU (Maximum Transmission Unit). Deux protocoles peuvent servir au transport des messages : UDP (User Datagram Protocol en mode non connecté, comme le courrier postal, utilisé pour le streaming par exemple) et TCP (Transmission Control Protocol en mode connecté, comme le téléphone, utilisé pour les pages Web).
Table of Contents
Hiérarchie d'interfaces
Définir dans le paquet session2.tp
- une interface Agent déclarant la méthode void envoyer(String msg),
- une interface Canal déclarant la méthode void emettre(String msg),
- une interface AgentCommuniquant héritant des deux interfaces précédentes.
Hiérarchie d'interfaces - Un agent communiquant
Héritage - Approche ascendante
On réalise les quatre combinaisons en factorisant la couche basse, suivant une approche ascendante pour l'héritage.
Réaliser le schéma suivant dans le paquet session2.tp.ascendant.
Approche ascendante - Factorisation de la couche basse
Héritage - Approche descendante
On réalise les quatre combinaisons en factorisant la couche haute, suivant une approche descendante pour l'héritage.
Réaliser le schéma suivant dans le paquet session2.tp.descendant.
Approche descendante - Factorisation de la couche haute
Héritage multiple (Java 8 seulement)
On réalise les quatre combinaisons en factorisant la couche haute et celle basse.
Réaliser le schéma suivant dans le paquet session2.tp.heritageMultiple.
Héritage multiple - Factorisation des deux couches
Agrégation avec délégation
On réalise les quatre combinaisons en factorisant la couche haute et celle basse.
Réaliser le schéma suivant dans le paquet session2.tp.agregation. Suivre les commentaires ci-dessous.
Agrégation avec délégation - Factorisation des deux couches
Test
Dans les trois paquets contenant les implémentations utilisant l'héritage, ajouter une classe de test, définie ainsi, puis tester les implémentations.
class Test { public static void main(String[] args) { AgentCommuniquant a = new AgentDecoupantMessagesPourProtocole1(); a.envoyer("nul n'est censé ignorer les principes de modularité."); Agent b = new AgentDecoupantMessagesPourProtocole2(); b.envoyer("nul n'est censé ignorer les principes de modularité."); a = new AgentEncapsulantMessagesPourProtocole1(); a.envoyer("nul n'est censé ignorer les principes de modularité."); b = new AgentEncapsulantMessagesPourProtocole2(); b.envoyer("nul n'est censé ignorer les principes de modularité."); } }
Dans le paquet session2.tp.agregation, réliser le même test.