Использование примеров и примеров шаблона декоратора GoF для ввода-вывода

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

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

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

Ответ 1

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

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

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

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

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

Мы хотим скорость, поэтому пусть она буферизуется в памяти:

BufferedInputStream bis = new BufferedInputStream(fis);

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

GzipInputStream gis = new GzipInputStream(bis);

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

ObjectInputStream ois = new ObjectInputStream(gis);

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

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

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

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

ois.close();

См. также:

Ответ 2

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

введите описание изображения здесь

Decorator шаблон имеет четыре компонента

  • Компонент: Компонент определяет интерфейс для объектов, которые могут динамически добавляться.
  • ConcreteComponent: это просто реализация интерфейса Component
  • Decorator: Decorator имеет ссылку на компонент, а также соответствует интерфейсу Component. Декоратор по существу обертывает компонент
  • ConcreteDecorator: ConcreteDecorator просто добавляет обязанности к исходному компоненту.

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

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

компонент:

InputStream:

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

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

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

ConcreteComponent:

FileInputStream:

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

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

Примеры всех ConcreteComponents InputStream:

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

Decorator:

FilterInputStream:

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

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

public class FilterInputStream
extends InputStream

ConcreteDecorator:

BufferedInputStream

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

Примеры всех ConcreteDecorators:

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);;
}

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

  • Обязанности и поведение объекта должны динамически добавляться/удаляться
  • Конкретные реализации должны быть отделены от обязанностей и поведения
  • Когда подклассификация слишком дорогостоящая, чтобы динамически добавлять/удалять обязанности

Ответ 3

В .NET существует множество декодеров потоков, таких как BufferedStream, CryptoStream, GzipStream и т.д. Все они украшают класс Stream.

Ответ 4

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

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

Они построены путем инкапсуляции потока и сами потоки.

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

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

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

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

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

Ответ 8

A - узор декоратора

A.1 - Использовать шаблон декоратора

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

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

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

B - Основной пример шаблона декоратора GoF

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

Вот тривиальный пример шаблона проектирования Gang Of Four Decorator;

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 Decorator Demo

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

{
    data: <data here>
}

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

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 Demo: 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.