Nous avons déjà passé en revue l'utilisation d'un objet singleton, mais vous ne réalisez peut-être pas encore que cette stratégie est un modèle de conception, et l'un des plus utilisés.

En fait, ces modèles sont nombreux et peuvent être classés en fonction de leur objectif spécifique.

Classement des modèles

Type de motif Application
Créationnel Un type qui résout le problème de création d'objet
De construction Des modèles qui nous permettent de construire une hiérarchie de classes correcte et extensible dans notre architecture
Comportemental Ce groupe de modèles facilite une interaction sûre et pratique entre les objets d'un programme.

Typiquement, un modèle est caractérisé par le problème qu'il résout. Jetons un coup d'œil à quelques modèles que nous rencontrons le plus souvent lorsque nous travaillons avec Java :

Modèle But
Singleton Nous connaissons déjà ce modèle — nous l'utilisons pour créer et accéder à une classe qui ne peut pas avoir plus d'une instance.
Itérateur Nous connaissons également celui-ci. Nous savons que ce modèle nous permet de parcourir un objet de collection sans révéler sa représentation interne. Il est utilisé avec les collections.
Adaptateur Ce modèle connecte des objets incompatibles afin qu'ils puissent fonctionner ensemble. Je pense que le nom du modèle d'adaptateur vous aide à imaginer exactement ce qu'il fait. Voici un exemple simple tiré de la vie réelle : un adaptateur USB pour une prise murale.
Méthode de modèle

Un modèle de programmation comportementale qui résout le problème d'intégration et vous permet de modifier les étapes algorithmiques sans modifier la structure d'un algorithme.

Imaginons que nous ayons un algorithme d'assemblage de voiture sous la forme d'une séquence d'étapes d'assemblage :

Châssis -> Carrosserie -> Moteur -> Intérieur de la cabine

Si on met un châssis renforcé, un moteur plus puissant, ou un intérieur avec un éclairage supplémentaire, on n'a pas à changer d'algorithme, et la séquence abstraite reste la même.

Décorateur Ce modèle crée des wrappers pour les objets afin de leur donner des fonctionnalités utiles. Nous l'examinerons dans le cadre de cet article.

Dans Java.io, les classes suivantes implémentent des modèles :

Modèle Où il est utilisé dans java.io
Adaptateur
Méthode de modèle
Décorateur

Motif décorateur

Imaginons que nous décrivons un modèle pour une conception de maison.

En général, l'approche ressemble à ceci:

Au départ, nous avons le choix entre plusieurs types de maisons. La configuration minimale est d'un étage avec un toit. Ensuite, nous utilisons toutes sortes de décorateurs pour modifier des paramètres supplémentaires, ce qui affecte naturellement le prix de la maison.

Nous créons une classe House abstraite :

public abstract class House {
	String info;

	public String getInfo() {
    	return info;
	}

	public abstract int getPrice();
}

Ici, nous avons 2 méthodes :

  • getInfo() renvoie des informations sur le nom et les caractéristiques de notre maison ;
  • getPrice() renvoie le prix de la configuration actuelle de la maison.

Nous avons également des implémentations standard de maison - brique et bois :

public class BrickHouse extends House {

	public BrickHouse() {
    	info = "Brick House";
	}

	@Override
	public int getPrice() {
    	return 20_000;
	}
}

public class WoodenHouse extends House {

	public WoodenHouse() {
    	info = "Wooden House";
	}

	@Override
	public int getPrice() {
    	return 25_000;
	}
}

Les deux classes héritent de la classe House et remplacent sa méthode de prix, en définissant un prix personnalisé pour une maison standard. Nous définissons le nom dans le constructeur.

Ensuite, nous devons écrire des classes de décorateur. Ces classes hériteront également de la classe House . Pour ce faire, nous créons une classe de décorateur abstraite.

C'est là que nous allons mettre une logique supplémentaire pour changer un objet. Initialement, il n'y aura pas de logique supplémentaire et la classe abstraite sera vide.

abstract class HouseDecorator extends House {
}

Ensuite, nous créons des implémentations de décorateur. Nous allons créer plusieurs classes qui nous permettront d'ajouter des fonctionnalités supplémentaires à la maison :

public class SecondFloor extends HouseDecorator {
	House house;

	public SecondFloor(House house) {
    	this.house = house;
	}

	@Override
	public int getPrice() {
    	return house.getPrice() + 20_000;
	}

	@Override
	public String getInfo() {
    	return house.getInfo() + " + second floor";
	}
}
Un décorateur qui ajoute un deuxième étage à notre maison

Le constructeur décorateur accepte une maison que nous allons « décorer », c'est-à-dire apporter des modifications. Et nous remplaçons les méthodes getPrice() et getInfo() , renvoyant des informations sur la nouvelle maison mise à jour en fonction de l'ancienne.

public class Garage extends HouseDecorator {

	House house;
	public Garage(House house) {
    	this.house = house;
	}

	@Override
	public int getPrice() {
    	return house.getPrice() + 5_000;
	}

	@Override
	public String getInfo() {
    	return house.getInfo() + " + garage";
	}
}
Un décorateur qui ajoute un garage à notre maison

Maintenant, nous pouvons mettre à jour notre maison avec des décorateurs. Pour ce faire, nous devons créer une maison :

House brickHouse = new BrickHouse();

Ensuite, nous fixons notrelogervariable égale à un nouveau décorateur, passant dans notre maison :

brickHouse = new SecondFloor(brickHouse);

Notrelogervariable est maintenant une maison avec un deuxième étage.

Examinons des cas d'utilisation impliquant des décorateurs :

Exemple de code Sortir
House brickHouse = new BrickHouse();

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());

Une maison en brique

20000

House brickHouse = new BrickHouse();

  brickHouse = new SecondFloor(brickHouse);

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());

Maison en brique + deuxième étage

40000

House brickHouse = new BrickHouse();


  brickHouse = new SecondFloor(brickHouse);
  brickHouse = new Garage(brickHouse);

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());

Maison en brique + deuxième étage + garage

45000

House woodenHouse = new SecondFloor(new Garage(new WoodenHouse()));

  System.out.println(woodenHouse.getInfo());
  System.out.println(woodenHouse.getPrice());

Maison en bois + garage + deuxième étage

50000

House woodenHouse = new WoodenHouse();

  House woodenHouseWithGarage = new Garage(woodenHouse);

  System.out.println(woodenHouse.getInfo());
  System.out.println(woodenHouse.getPrice());

  System.out.println(woodenHouseWithGarage.getInfo());
  System.out.println(woodenHouseWithGarage.getPrice());

Maison en bois

25000

Maison en bois + garage

30000

Cet exemple illustre l'avantage d'améliorer un objet avec un décorateur. Nous n'avons donc pas modifié lemaison en boisl'objet lui-même, mais a plutôt créé un nouvel objet basé sur l'ancien. Ici, nous pouvons voir que les avantages s'accompagnent d'inconvénients : nous créons à chaque fois un nouvel objet en mémoire, ce qui augmente la consommation de mémoire.

Regardez ce diagramme UML de notre programme :

Un décorateur a une implémentation super simple et modifie dynamiquement les objets, les mettant à niveau. Les décorateurs peuvent être reconnus par leurs constructeurs, qui prennent comme paramètres des objets du même type abstrait ou de la même interface que la classe actuelle. En Java, ce modèle est largement utilisé dans les classes d'E/S.

Par exemple, comme nous l'avons déjà noté, toutes les sous-classes de java.io.InputStream , OutputStream , Reader et Writer ont un constructeur qui accepte les objets des mêmes classes.