64 votes

Cas d'utilisation et exemples du modèle de décorateur GoF pour IO

J'ai lu dans wikipedia que Modèle de décorateur est utilisé pour .Net y Java IO classes.

Quelqu'un peut-il expliquer comment il est utilisé ? Et quel en est l'intérêt, avec un exemple possible ?

Il existe un exemple de Formulaires Windows sur wikipedia mais je veux savoir comment cela se passe avec Java IO classes.

161voto

BalusC Points 498232

InputStream est une classe abstraite. La plupart des implémentations concrètes comme BufferedInputStream , GzipInputStream , ObjectInputStream etc. ont un constructeur qui prend une instance de la classe même classe abstraite. C'est la clé de reconnaissance du motif décorateur (cela s'applique également aux constructeurs prenant une instance de la même interface).

Lorsqu'un tel constructeur est utilisé, toutes les méthodes seront déléguées à l'instance enveloppée, avec des changements dans la façon dont les méthodes se comportent. Par exemple, la mise en mémoire tampon du flux au préalable, la décompression du flux au préalable ou l'interprétation du flux de manière différente. Certains ont même des méthodes supplémentaires qui, finalement, sont également déléguées à l'instance enveloppée. Ces méthodes agrémentent l'instance enveloppée d'un comportement supplémentaire.

Disons que nous avons un tas d'objets Java sérialisés dans un fichier Gzippé et que nous voulons les lire rapidement.

Ouvrez d'abord un flux d'entrée de celui-ci :

FileInputStream fis = new FileInputStream("/objects.gz");

Nous voulons de la rapidité, alors mettons-le en mémoire :

BufferedInputStream bis = new BufferedInputStream(fis);

Le fichier est gzippé, nous devons donc le dézipper :

GzipInputStream gis = new GzipInputStream(bis);

Nous devons désérialiser ces objets Java :

ObjectInputStream ois = new ObjectInputStream(gis);

Maintenant, nous pouvons enfin l'utiliser :

SomeObject someObject = (SomeObject) ois.readObject();
// ...

L'avantage est que vous avez une grande liberté pour décorer le flux en utilisant un ou plusieurs décorateurs différents selon vos besoins. C'est bien mieux que d'avoir une seule classe pour toutes les combinaisons possibles, comme par exemple ObjectGzipBufferedFileInputStream , ObjectBufferedFileInputStream , GzipBufferedFileInputStream , ObjectGzipFileInputStream , ObjectFileInputStream , GzipFileInputStream , BufferedFileInputStream etc.

Notez que lorsque vous êtes sur le point de fermer le flux, il suffit de fermer l'option le plus à l'extérieur décorateur est suffisant. Il déléguera l'appel proche jusqu'au fond.

ois.close();

Voir aussi :

19voto

Ravindra babu Points 5571

Comprenons les composantes de Décorateur avant de parcourir les classes IO de Java.

enter image description here

Décorateur Le modèle comporte quatre éléments

  1. Composant : Le site Composant définit l'interface pour les objets qui peuvent avoir des responsabilités ajoutées dynamiquement
  2. Composant concret : Il s'agit simplement d'une mise en œuvre de Composant interface
  3. Décorateur : Le site Décorateur a une référence à un Composant et est également conforme à la norme Composant interface. Le décorateur consiste essentiellement à envelopper l'interface Composant
  4. BétonDécorateur : Le site ConcreteDecorator ajoute juste des responsabilités à l'original Composant .

Le patron décorateur peut être utilisé pour étendre (décorer) la fonctionnalité d'un certain objet de manière statique ou, dans certains cas, au moment de l'exécution, indépendamment des autres instances de la même classe, à condition qu'un travail de base soit effectué au moment de la conception. Pour ce faire, on conçoit un nouveau Décorateur qui englobe la classe d'origine.

Maintenant, faisons correspondre ces concepts à des classes de pacakge java.io.

Composant :

InputStream :

Cette classe abstraite est la superclasse de toutes les classes représentant un flux d'entrée d'octets.

Les applications qui doivent définir une sous-classe de InputStream doivent toujours fournir une méthode qui renvoie l'octet d'entrée suivant.

public abstract int read() est une méthode abstraite.

Composant concret :

FileInputStream :

Un FileInputStream obtient des octets d'entrée d'un fichier dans un système de fichiers. Les fichiers disponibles dépendent de l'environnement de l'hôte.

FileInputStream est destiné à la lecture de flux d'octets bruts tels que des données d'image. Pour lire des flux de caractères, vous pouvez utiliser FileReader.

Exemples de tous les ConcreteComponents de InputStream :

AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, 
InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, 
StringBufferInputStream

Décorateur :

FilterInputStream :

Un FilterInputStream contient un autre flux d'entrée, qu'il utilise comme source de données de base, en transformant éventuellement les données en cours de route ou en fournissant des fonctionnalités supplémentaires.

Veuillez noter que FilterInputStream met en œuvre InputStream => Le décorateur implémente le composant comme indiqué dans le diagramme UML. .

public class FilterInputStream
extends InputStream

BétonDécorateur :

BufferedInputStream

Un BufferedInputStream ajoute des fonctionnalités à un autre flux d'entrée, à savoir la possibilité de mettre l'entrée en mémoire tampon et de prendre en charge les méthodes mark et reset.

Exemples de tous ConcreteDecorators :

BufferedInputStream, CheckedInputStream, CipherInputStream, DataInputStream, 
DeflaterInputStream, DigestInputStream, InflaterInputStream, 
LineNumberInputStream, ProgressMonitorInputStream, PushbackInputStream

Exemple de code fonctionnel :

J'ai utilisé BufferedInputStream pour lire chaque caractère d'un mot, qui a été stocké dans un fichier texte a.txt

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
        char c = (char)bis.read();
        System.out.println("Char: "+c);;
}

Quand utiliser ce modèle :

  1. Les responsabilités et les comportements des objets doivent être ajoutés/supprimés de manière dynamique.
  2. Les mises en œuvre concrètes doivent être découplées des responsabilités et des comportements.
  3. Lorsque la sous-classification est trop coûteuse pour ajouter/supprimer dynamiquement des responsabilités.

8voto

Alex Aza Points 29204

Dans .NET, il existe un grand nombre de décorateurs de flux, comme BufferedStream, CryptoStream, GzipStream, etc. Tous ces décorateurs décorent Stream classe.

7voto

Levent Divilioglu Points 1026

A - Motif décoratif

A.1 - Cas d'utilisation du motif décorateur

Le modèle décorateur est utilisé pour étendre une fonctionnalité héritée sans modifier la classe héritée. Disons que nous avons une classe concrète qui implémente une interface. Et nous avons besoin d'étendre la fonctionnalité de la méthode existante, mais parce que la classe existante et ses méthodes sont déjà utilisées par d'autres classes, nous ne voulons pas faire de changement dans les classes existantes. Mais nous avons également besoin d'une fonctionnalité étendue sur une classe plus récente, alors comment résoudre ce problème ?

1- We can't change the existing legacy code
2- We want to extend the functionality

Nous utilisons donc le modèle des décorateurs, en enveloppant la classe existante dans les décorateurs.

B - Exemple de motif décorateur GoF de base

Ici, nous avons une interface simple et une implémentation/classe concrète. L'interface a une méthode simple, qui est getMessageOfTheDay et il renvoie un String . Supposons qu'il y ait beaucoup d'autres classes utilisant cette méthode. Donc si nous voulons faire un changement dans l'implémentation/classe concrète, cela affectera l'ancien code hérité. Nous voulons le modifier uniquement pour les nouvelles classes et nous utilisons donc le modèle du décorateur.

Voici un exemple trivial du motif Gang Of Four Decorator Design ;

B.1 - Greeter.java

public interface Greeter {
    String getMessageOfTheDay();
}

B.2 - BasicGreeter.java

public class BasicGreeter implements Greeter {

    @Override
    public String getMessageOfTheDay() {
        return "Welcome to my server";
    }

}

B.3 - Classe décoratrice abstraite : GreeterDecorator.java

public abstract class GreeterDecorator implements Greeter {

    protected Greeter greeter;

    public GreeterDecorator(Greeter greeter) {
        this.greeter = greeter;
    }

    public String getMessageOfTheDay() {
        return greeter.getMessageOfTheDay();
    }

}

B.4 - Classe de décorateur concret : StrangerDecorator.java

public class StrangerDecorator extends GreeterDecorator {

    public StrangerDecorator(Greeter greeter) {
        super(greeter);
    }

    @Override
    public String getMessageOfTheDay() {
        return "Hello Stranger " + super.getMessageOfTheDay();
    }

}

B.5 - Code de démonstration : DecoratorDemo .java

public class DecoratorDemo {

    public static void main(String[] args) {
        Greeter greeter = new BasicGreeter();

        String motd = greeter.getMessageOfTheDay();

        System.out.println(motd);

        Greeter newGreeter = new StrangerDecorator(greeter);

        String newMotd = newGreeter.getMessageOfTheDay();

        System.out.println(newMotd);

        Greeter muchNewGreeter = new StrangerDecorator(new StrangerDecorator(greeter));

        String newestMotd = muchNewGreeter.getMessageOfTheDay();

        System.out.println(newestMotd);
    }

}

Jetez un coup d'œil à ces exemples. La classe décorateur abstrait est nécessaire pour envelopper le contrat et l'implémentation d'origine. En utilisant le décorateur abstrait, vous pouvez créer de nouveaux décorateurs multiples mais dans cet exemple, BasicGreeter est enveloppé dans le décorateur abstrait et nous avons seulement créé une nouvelle classe de décorateur qui est StrangeGreeter . Veuillez noter que les classes de décorateurs peuvent être utilisées comme un train, nous pouvons envelopper un décorateur dans un autre décorateur ou le même. La fonctionnalité est extensible mais la classe originale est préservée sans aucune modification.

C - Démonstration de OutputStream

Jetons un coup d'œil à cet exemple. Nous voulons écrire une chaîne de caractères dans un fichier avec OutputStream. Voici le code de démonstration ;

C.1 - Exemple de démonstration de OutputStream pour écrire un fichier

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class FileWriterDemo {

    public static void main(String[] args) throws IOException {
        File file = new File("./normal.txt");
        file.createNewFile();

        OutputStream oStream = new FileOutputStream(file);

        String content = "I love Commodore 64";

        oStream.write(content.getBytes());

        oStream.close();
    }

}

C.2 - Sortie du décorateur JSON : normal.txt

Il y aura un nouveau fichier avec le nom "normal.txt" créé sous le dossier du projet et le contenu sera ;

I love Commodore 64

D - Démonstration du décorateur JSON OutputStream

Maintenant, je veux créer un format de wrapper JSON, qui est le suivant ;

{
    data: <data here>
}

Ce que je veux, c'est écrire le contenu dans un simple champ. JSON format. Comment pouvons-nous atteindre cet objectif ? Il existe de nombreux moyens triviaux. Cependant, j'utiliserai le Patron de décorateur GoF en écrivant un JSONDecorator qui étend le OutputStream de Java ;

D.1 - Décorateur JSON pour OutputStream : JSONStream.java

public class JSONStream extends OutputStream {

    protected OutputStream outputStream;

    public JSONStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    @Override
    public void write(int b) throws IOException {
        outputStream.write(b);
    }

    @Override
    public void write(byte[] b) throws IOException {
        String content = new String(b);

        content = "{\r\n\tdata:\"" + content + "\"\r\n}";

        outputStream.write(content.getBytes());
    }

}

D.2 - Démonstration du décorateur JSON : JSONDecoratorDemo.java

public class JSONDecoratorDemo {

    public static void main(String[] args) throws IOException {
        File file = new File("./json.txt");
        file.createNewFile();

        OutputStream oStream = new FileOutputStream(file);

        JSONStream js = new JSONStream(oStream);

        String content = "I love Commodore 64";

        js.write(content.getBytes());

        js.close();
        oStream.close();
    }

}

D.3 - Sortie du décorateur JSON : json.txt

{
    data:"I love Commodore 64"
}

En fait, OutputStream lui-même un motif de décorateur, c'est le décorateur abstrait et le décorateur concret ici est le JSONStream classe.

5voto

Snicolas Points 19644

Le motif décorateur est utilisé dans les classes java.io lorsque vous manipulez des flux d'entrée/sortie (et il en va de même pour les lecteurs et les écrivains).

inputstream, bytearrayinputstream, stringbuilderinputstreams et ainsi de suite sont des éléments basés. Filterinputstream est la classe de base pour les classes décoratrices. Les flux d'entrée filtrants (tels que les flux d'entrée tamponnés) peuvent faire des choses supplémentaires lorsqu'ils lisent des flux ou y écrivent.

Ils sont construits en encapsulant un flux, et sont eux-mêmes des flux.

new BufferedReader( new FileInputStream() ).readLine();

Je ne vois pas de classe implémentant ce modèle dans java.net, mais je pense qu'on vous a parlé de ce paquet car il est fortement lié à java.io (socket.getInputStream par exemple).

En fait, voici un cours de O'Relly ( pdf sur uwosh.edu | archive.org , diapositives sur slideshare.net ) qui explique comment le décorateur est implémenté dans java.io.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X