Примеры использования и примеры шаблонов GoF Decorator для ввода-вывода - PullRequest
51 голосов
/ 16 июня 2011

Я прочитал в Википедии , что Шаблон декоратора используется для .Net и Java IO классов.

Кто-нибудь может объяснить, как это используется?И какая польза от этого возможного примера?

В википедии есть пример Windows формы , но я хочу знать, как это происходит с Java IO классы.

Ответы [ 8 ]

128 голосов
/ 16 июня 2011

InputStream - абстрактный класс. Большинство конкретных реализаций, таких как BufferedInputStream, GzipInputStream, ObjectInputStream и т. Д., Имеют конструктор, который принимает экземпляр same абстрактный класс. Это ключ распознавания шаблона декоратора (это также относится к конструкторам, использующим экземпляр того же интерфейса).

Когда используется такой конструктор, все методы будут делегированы обернутому экземпляру с изменениями в поведении методов. Например, предварительно буферизуя поток в памяти, предварительно распаковывая поток или интерпретируя поток по-другому. У некоторых даже есть дополнительные методы, которые, наконец, также делегируют дальше обернутому экземпляру. Эти методы украшают завернутый экземпляр дополнительным поведением.

Допустим, у нас есть несколько сериализованных Java-объектов в файле Gzipped и мы хотим их быстро прочитать.

Сначала откройте поток ввода:

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

Нам нужна скорость, поэтому давайте поместим ее в буфер:

BufferedInputStream bis = new BufferedInputStream(fis);

Файл распакован, поэтому нам нужно распаковать его:

GzipInputStream gis = new GzipInputStream(bis);

Нам нужно десериализовать эти объекты Java:

ObjectInputStream ois = new ObjectInputStream(gis);

Теперь мы можем наконец использовать его:

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

Преимущество заключается в том, что у вас есть большая свобода для украшения потока с использованием одного или нескольких различных декораторов в соответствии с вашими потребностями. Это гораздо лучше, чем иметь один класс для каждой возможной комбинации, такой как ObjectGzipBufferedFileInputStream, ObjectBufferedFileInputStream, GzipBufferedFileInputStream, ObjectGzipFileInputStream, ObjectFileInputStream, GzipFileInputStream, BufferedFileInputStream и т. Д.

Обратите внимание, что когда вы собираетесь закрыть поток, достаточно просто закрыть самый внешний декоратор. Он будет делегировать вызов close до самого конца.

ois.close();

Смотри также:

13 голосов
/ 27 мая 2016

Давайте разберемся с компонентами шаблона Decorator , прежде чем проходить классы Java IO.

enter image description here

Декоратор шаблон состоит из четырех компонентов

  1. Компонент: Компонент определяет интерфейс для объектов, для которых можно динамически добавлять обязанности
  2. ConcreteComponent: Это просто реализация Компонент интерфейс
  3. Декоратор: Декоратор имеет ссылку на Компонент , а также соответствует интерфейсу Компонент . Декоратор по сути обертывает Компонент
  4. ConcreteDecorator: ConcreteDecorator просто добавляет обязанности к оригинальному Компоненту .

Шаблон декоратора может использоваться для расширения (декорирования) функциональности определенного объекта статически или, в некоторых случаях, во время выполнения, независимо от других экземпляров того же класса, при условии, что во время разработки сделаны некоторые фундаментальные работы. Это достигается за счет разработки нового класса Decorator , который охватывает исходный класс.

Теперь давайте сопоставим эти понятия с классами java.io pacakge.

Компонент:

InputStream :

Этот абстрактный класс является суперклассом всех классов, представляющих входной поток байтов.

Приложения, которым необходимо определить подкласс InputStream, всегда должны предоставлять метод, который возвращает следующий байт ввода.

public abstract int read() - абстрактный метод.

ConcreteComponent:

FileInputStream

FileInputStream получает входные байты из файла в файловой системе. Доступность файлов зависит от среды хоста.

FileInputStream предназначен для чтения потоков необработанных байтов, таких как данные изображения. Для чтения потоков символов рассмотрите возможность использования FileReader.

Примеры всех Конкретных Компонентов InputStream:

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

декоратор:

FilterInputStream

FilterInputStream содержит некоторый другой входной поток, который он использует в качестве основного источника данных, возможно преобразовывая данные по пути или предоставляя дополнительные функциональные возможности.

Обратите внимание, что FilterInputStream реализует InputStream => Декоратор реализует Компонент, как показано на диаграмме UML .

public class FilterInputStream
extends InputStream

ConcreteDecorator:

BufferedInputStream

BufferedInputStream добавляет функциональность к другому входному потоку, а именно, возможность буферизовать ввод и поддерживать методы меток и сброса.

Примеры всех Конкретных декораторов :

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

Рабочий пример кода:

Я использовал BufferedInputStream для чтения каждого символа слова, которое было сохранено в текстовом файле 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);;
}

Когда использовать этот шаблон:

  1. Обязанности и поведение объекта должны динамически добавляться / удаляться
  2. Конкретные реализации должны быть отделены от ответственности и поведения
  3. Когда подклассы слишком дороги для динамического добавления / удаления обязанностей
8 голосов
/ 16 июня 2011

В .NET есть несколько потоковых декораторов, таких как BufferedStream, CryptoStream, GzipStream и т. Д. Все они украшают Stream class.

4 голосов
/ 23 декабря 2017

A - Выкройка декоратора

A.1 - Пример использования шаблона Decorator

Шаблон Decorator используется для расширения устаревшей функциональности без изменения унаследованного класса. Допустим, у нас есть конкретный класс, который реализует интерфейс. И нам нужно расширить функциональность существующего метода, потому что существующий класс и его методы уже используются другими классами, поэтому мы не хотим вносить изменения в существующие классы. Но нам также нужны расширенные функциональные возможности для нового класса, тогда как нам решить эту проблему?

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

Поэтому мы используем шаблон декоратора, оборачивая существующий класс внутри декораторов.

B - Пример базового шаблона GoF Decorator

Здесь у нас есть простой интерфейс и класс реализации / бетона. Интерфейс имеет один простой метод, который является getMessageOfTheDay и возвращает String. Предположим, что есть много других классов, использующих этот метод. Поэтому, если мы хотим внести изменения в реализацию / конкретный класс, это повлияет на старый унаследованный код. Мы хотим изменить его только для новых классов, поэтому используем шаблон декоратора.

Вот тривиальный пример шаблона 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 - Класс абстрактного декоратора: 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 - Класс бетонного декоратора: StrangerDecorator.java

public class StrangerDecorator extends GreeterDecorator {

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

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

}

B.5 - демонстрационный код: 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);
    }

}

Посмотрите на эти примеры. Класс абстрактного декоратора необходим для переноса исходного контракта и реализации. Используя абстрактный декоратор, вы можете создать несколько новых декораторов, но в этом примере BasicGreeter обернут внутри абстрактного декоратора, и мы создали только новый класс декоратора, который является StrangeGreeter . Пожалуйста, сообщите, что классы декораторов могут использоваться как поезд, мы можем обернуть декоратор в другой декоратор или так же. Функциональность расширяема, но оригинальный класс сохраняется без каких-либо изменений.

C - Демонстрация OutputStream

Давайте посмотрим на этот пример. Мы хотим записать строку в файл с помощью OutputStream. Вот демонстрационный код;

C.1 - Пример демонстрации OutputStream для записи файла

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 - Вывод JSON-декоратора: normal.txt

В папке проекта будет создан новый файл с именем "normal.txt", содержимое которого будет;

I love Commodore 64

D - Демонстрационная демонстрация JSON OutputStream

Теперь я хочу создать формат оболочки JSON, который выглядит следующим образом;

{
    data: <data here>
}

Я хочу написать содержимое в одном простом поле JSON в формате. Как мы можем достичь этой цели? Есть много тривиальных способов. Однако я буду использовать GoF Decorator Pattern , написав JSONDecorator , который расширяет класс OutputStream Java;

D.1 - JSON-декоратор для 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 - Демонстрация 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 - Вывод JSON-декоратора: json.txt

{
    data:"I love Commodore 64"
}

На самом деле, OutputStream сам является шаблоном декоратора, это абстрактный декоратор и конкретный декоратор, а вот класс JSONStream .

4 голосов
/ 16 июня 2011

Шаблон декоратора используется в классах java.io, когда вы манипулируете потоками ввода / вывода (то же самое относится и к читателям и писателям).

inputtream, bytearrayinputstream, stringbuilderinputstreams и так далее являются основанными элементами. Filterinputstream является базовым классом для классов декораторов. Фильтровать входные потоки (такие как буферизованный входной поток) могут делать дополнительные вещи, когда они читают потоки или записывают в них.

Они создаются путем инкапсуляции потока и сами являются потоками.

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

Я не могу представить ни одного класса, реализующего этот шаблон в java.net, но я думаю, что вам сказали об этом пакете, так как он сильно привязан к java.io (например, socket.getInputStream).

На самом деле, вот курс от О'Релли , который объясняет, как декоратор реализован в java.io.

С уважением, Stéphane

2 голосов
/ 10 января 2017

Ну, я могу опоздать на вечеринку, но этот вопрос никогда не устареет.Ключевым моментом для понимания Decorator является то, что он дает вам возможность подключить объект к существующему объекту к другому существующему объекту и так далее.Это популярно для реализации этого шаблона в конструкторе.Например,

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

Если вы посмотрите на диаграмму в википедии, вы увидите, что ConcreteComponent и Decorator наследуют от того же суперкласса / интерфейса, Компонент .То есть эти два класса имеют одинаковые методы реализации.

Однако в классе Decorator вы увидите стрелку назад к Компоненту , что означает, что вы используете Компонент где-то в Декоратор класс.В этом случае вы используете Component в качестве типа данных конструктора в Decorator .Это большая хитрость.Без этого трюка вы не сможете подключить новый объект к существующему объекту.

После этого вы можете создавать подклассы, наследуемые от класса Decorator .Поскольку все классы имеют одинаковый корень, каждый отдельный класс может свободно подключаться без какого-либо порядка.

2 голосов
/ 16 июня 2011

Шаблон декоратора используется для добавления функциональности к существующим объектам, таким как класс, определенный в библиотеке.Затем вы можете «украсить» его под свои нужды.Если вам интересно узнать больше о шаблонах, я рекомендую «Дизайнерские шаблоны» от Gang of Four.

2 голосов
/ 16 июня 2011

Один из способов украсить поток ввода / вывода - применить к нему сжатие / декомпрессию.См., Например, классы в java.util.zip.Такой декорированный поток может использоваться точно так же, как «обычный» поток ввода / вывода, при этом сжатие / декомпрессия выполняется полностью прозрачно.

...