web-dev-qa-db-fra.com

Cas d'utilisation et exemples de modèle GoF Decorator pour IO

J'ai lu dans wikipedia que le motif décorateur est utilisé pour . Net et Java IO classes.

Quelqu'un peut-il expliquer comment cela est utilisé? Et quel en est l'avantage 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.

52
DarthVader

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 même classe abstraite. C'est la clé de reconnaissance du modèle de 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 sont déléguées à l'instance encapsulée, avec des changements dans le comportement des méthodes. Par exemple, mettre en mémoire tampon le flux au préalable, décompresser le flux au préalable ou interpréter le flux différemment. Certains ont même des méthodes supplémentaires qui délèguent enfin davantage à l'instance encapsulée. Ces méthodes décorent l'instance encapsulée avec un comportement supplémentaire.

Disons que nous avons un tas d'objets sérialisés Java objets 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 vitesse, nous allons donc la mettre en mémoire tampon:

BufferedInputStream bis = new BufferedInputStream(fis);

Le fichier est compressé, nous devons donc le décompresser:

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 beaucoup de liberté pour décorer le flux en utilisant un ou plusieurs décorateurs différents pour répondre à vos besoins. C'est bien mieux que d'avoir une seule classe pour chaque combinaison possible comme ObjectGzipBufferedFileInputStream, ObjectBufferedFileInputStream, GzipBufferedFileInputStream, ObjectGzipFileInputStream, ObjectFileInputStream, GzipFileInputStream, BufferedFileInputStream, etc.

Notez que lorsque vous êtes sur le point de fermer le flux, il suffit de fermer le décorateur le plus à l'extérieur. Il déléguera l'appel de fermeture jusqu'en bas.

ois.close();

Voir également:

133
BalusC

Comprenons les composants du modèle Décorateur avant de passer par les classes Java IO).

enter image description here

Décorateur le modèle a quatre composants

  1. Composant: Le Composant définit l'interface pour les objets qui peuvent avoir des responsabilités ajoutées dynamiquement
  2. ConcreteComponent: Il s'agit simplement d'une implémentation de l'interface Component
  3. Décorateur: Le Décorateur a une référence à un Composant, et est également conforme au Composant interface. Decorator enveloppe essentiellement le Composant
  4. ConcreteDecorator: Le ConcreteDecorator ajoute simplement des responsabilités à l'original Component.

Le modèle de 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 d'autres instances de la même classe, à condition qu'un travail de base soit effectué au moment de la conception. Ceci est réalisé en concevant une nouvelle classe Decorator qui encapsule la classe d'origine.

Mappons maintenant ces concepts aux classes 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 retourne l'octet suivant d'entrée.

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

ConcreteComponent:

FileInputStream :

Un FileInputStream obtient des octets d'entrée à partir d'un fichier dans un système de fichiers. Les fichiers disponibles dépendent de l'environnement 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, pensez à utiliser FileReader.

Exemples de tous les composants concrets 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, transformant éventuellement les données en cours de route ou fournissant des fonctionnalités supplémentaires.

Veuillez noter que FilterInputStream implémente InputStream => Decorator implémente Component comme indiqué dans le diagramme UML.

public class FilterInputStream
extends InputStream

ConcreteDecorator:

BufferedInputStream

Un BufferedInputStream ajoute des fonctionnalités à un autre flux d'entrée, à savoir la possibilité de tamponner l'entrée et de prendre en charge les méthodes de marquage et de réinitialisation.

Exemples de tous ConcreteDecorators:

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

Exemple de code de travail:

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 dynamiquement
  2. Les mises en œuvre concrètes doivent être dissociées des responsabilités et des comportements
  3. Lorsque la sous-classification est trop coûteuse pour ajouter/supprimer dynamiquement des responsabilités
14
Ravindra babu

Dans .NET, il existe un tas de décorateurs de flux, comme BufferedStream, CryptoStream, GzipStream, etc. Tous ceux-ci décorent la classe Stream.

8
Alex Aza

A - Motif de décorateur

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

Le modèle de décorateur est utilisé pour étendre une fonctionnalité héritée sans changer la classe héritée. Disons que nous avons une classe concrète qui implémente une interface. Et nous devons cependant étendre les fonctionnalités de la méthode existante parce que la classe existante et ses méthodes sont déjà utilisées par d'autres classes, donc nous ne voulons pas faire de changement dans les classes existantes. Mais nous avons également besoin de fonctionnalités étendues 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 un motif de décorateur, enveloppons la classe existante à l'intérieur des décorateurs.

B - Exemple de motif de base GoF Decorator

Nous avons ici une interface simple et une classe d'implémentation/concrète. L'interface a une méthode simple, qui est getMessageOfTheDay et elle renvoie un String. Supposons que de nombreuses autres classes utilisent cette méthode. Donc, si nous voulons faire un changement dans la classe d'implémentation/concret, cela affectera l'ancien code hérité. Nous voulons le changer uniquement pour les nouvelles classes, nous utilisons donc le motif décorateur.

Voici un exemple trivial du modèle de conception de décorateur Gang Of Four;

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 de décorateur abstrait: 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 Décorateur de béton: 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 œil à ces exemples. La classe de décorateur abstrait est nécessaire pour envelopper le contrat et la mise en œuvre d'origine. En utilisant le décorateur abstrait, vous pouvez créer plusieurs décorateurs plus récents mais dans cet exemple, BasicGreeter est enveloppé à l'intérieur du décorateur abstrait et nous n'avons créé que sur une nouvelle classe de décorateur qui est StrangeGreeter. Veuillez noter que les cours de décorateur peuvent être utilisés 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 d'origine est préservée sans aucune modification.

C - Démo OutputStream

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

C.1 - Exemple de démo 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 JSON Decorator: 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émo JSON OutputStream Decorator

Maintenant, je veux créer un format 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 nombreuses façons triviales. Cependant, j'utiliserai le GoF Decorator Pattern en écrivant un JSONDecorator qui étend la classe 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émo JSON Decorator: 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 JSON Decorator: 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 la classe JSONStream.

5
Levent Divilioglu

Le modèle de 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 de filtre (tels que le flux d'entrée en mémoire tampon) peuvent faire des choses supplémentaires lorsqu'ils lisent des flux ou y écrivent.

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

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

Je ne peux penser à aucune 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 d'O'Relly qui explique comment le décorateur est implémenté dans Java.io.

Cordialement, Stéphane

4
Snicolas

Le motif décorateur est utilisé pour ajouter des fonctionnalités à des objets existants tels qu'une classe définie dans une bibliothèque. Vous pouvez ensuite le "décorer" pour l'adapter à vos besoins. Si vous souhaitez en savoir plus sur les modèles, je vous recommande les "modèles de conception" du Gang of Four.

2
Will Johnson

Eh bien, je suis peut-être en retard à la fête, mais cette question ne vieillit jamais. Le point clé pour comprendre Decorator est qu'il vous donne la possibilité de connecter un objet à un objet existant à un autre objet existant et ainsi de suite. Il est courant d'implémenter ce modèle dans un constructeur. Par exemple,

    Icecream ic = new RainbowTopUp(new ChocoTopUp(new Vanilla()));

Si vous regardez le diagramme dans le wikipedia, vous verriez ConcreteComponent et Décorateur hérite de la même superclasse/interface, Composant . Autrement dit, ces deux classes ont les mêmes méthodes d'implémentation.

Cependant, dans la classe Decorator , vous verrez une flèche renvoyant au composant , ce qui signifie que vous utilisez Component quelque part dans la classe Decorator . Dans ce cas, vous utilisez le composant comme type de données d'un constructeur dans le décorateur . Voilà le gros truc. Sans cette astuce, vous ne pourrez pas connecter un nouvel objet à un objet existant.

Après cela, vous pouvez créer des sous-classes héritant de la classe Decorator . Parce que toutes les classes ont la même racine, toutes les classes peuvent librement se plugin sans aucun ordre.

2
kosalgeek

Vous pouvez décorer un flux d'entrée/sortie en lui appliquant une compression/décompression. Voir les cours dans Java.util.Zip, par exemple. Un tel flux décoré peut être utilisé exactement de la même manière qu'un flux d'entrée/sortie "normal", la compression/décompression étant effectuée de manière totalement transparente.

2
Chris Jester-Young