Паттерн декоратор java inputstream

Паттерны проектирования. Декоратор

Мы уже прошли такую схему использования объекта класса, как Singleton, но ты, возможно, еще не подозреваешь, что это один из паттернов проектирования, причем один из самых используемых.

На самом деле существует очень много этих самых паттернов, более того — у них есть иерархия, и у каждого паттерна — свое предназначение.

Классификация паттернов

Вид паттерна Применяемость
Порождающие Тип, который решает проблему создания объектов
Структурные Паттерны, которые позволяют выстроить правильную, доступную к расширению иерархию классов в нашей архитектуре
Поведенческие Этот кластер паттернов отвечает за безопасное и удобное взаимодействие между объектами программы

Обычно паттерн характеризуется проблемой, которую он решает. Давайте рассмотрим несколько паттернов, с которыми мы чаще всего сталкиваемся при работе с Java:

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

Представим, что у нас есть алгоритм сборки автомобиля в виде последовательности сборки:

Шасси -> Кузов -> Двигатель -> сборка салона

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

В Java.io паттерны реализуются в следующих классах:

  • java.io.InputStreamReader(InputStream) (возвращает Reader )
  • java.io.OutputStreamWriter(OutputStream) (возвращает Writer )
  • java.io.InputStream
  • java.io.OutputStream
  • java.io.Reader
  • java.io.Writer
  • java.io.InputStream
  • OutputStream
  • Reader
  • Writer

Паттерн Декоратор

Давайте представим, что мы описываем модель проектирования дома.

В целом схема будет такой:

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

Создаем абстрактный класс дома:

 public abstract class House < String info; public String getInfo()< return info; >public abstract int getPrice(); > 
  • getInfo() возвращает информацию о названии и комплектации нашего дома;
  • getPrice() — цену дома в текущей комплектации.

Также у нас есть стандартные реализации домов — кирпичный и деревянный:

 public class BrickHouse extends House < public BrickHouse() < info = "Кирпичный дом"; >@Override public int getPrice() < return 20_000; >> public class WoodenHouse extends House < public WoodenHouse() < info = "Деревянный дом"; >@Override public int getPrice() < return 25_000; >> 

Оба класса наследуются от класса House и переопределяют метод цены, устанавливая индивидуальную цену за стандартный дом. В конструкторе присваиваем имя.

Далее нам необходимо написать классы — декораторы. Это классы, которые тоже унаследованы от класса House . Для этого мы создаем абстрактный класс декоратора.

В него мы можем положить дополнительную логику для изменения объекта. В нашем случае дополнительной логики не будет и абстрактный класс будет пустым.

 abstract class HouseDecorator extends House

Далее создаем классы-реализации декораторов. Мы создадим несколько классов, позволяющих добавить к дому дополнительные параметры:

 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() + " + второй этаж"; >> 

В конструктор декоратора принимается дом, к которому мы применяем модификации декоратора. А методы getPrice() и getInfo() мы переопределяем, возвращая информацию о новом модернизированном доме, составленном на основе старого.

 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() + " + гараж"; >> 

Теперь мы можем изменить наш дом с помощью декораторов. Для этого нужно создать дом:

 House brickHouse = new BrickHouse(); 

Далее мы присваиваем нашей переменной house новое значение в виде декоратора, в который мы передаем наш дом:

 brickHouse = new SecondFloor(brickHouse); 

Наша переменная house уже имеет дом со вторым этажом.

Давайте рассмотрим кейсы использования декораторов:

 House brickHouse = new BrickHouse(); System.out.println(brickHouse.getInfo()); System.out.println(brickHouse.getPrice()); 
 House brickHouse = new BrickHouse(); brickHouse = new SecondFloor(brickHouse); System.out.println(brickHouse.getInfo()); System.out.println(brickHouse.getPrice()); 

Кирпичный дом + второй этаж

 House brickHouse = new BrickHouse(); brickHouse = new SecondFloor(brickHouse); brickHouse = new Garage(brickHouse); System.out.println(brickHouse.getInfo()); System.out.println(brickHouse.getPrice()); 

Кирпичный дом + второй этаж + гараж

 House woodenHouse = new SecondFloor(new Garage(new WoodenHouse())); System.out.println(woodenHouse.getInfo()); System.out.println(woodenHouse.getPrice()); 

Деревянный дом + гараж + второй этаж

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

В этом примере мы видим преимущество модернизации объекта с помощью декоратора. Получается, что мы не изменили сам объект woodenHouse , а создали новый объект на базе него. Но сквозь этого преимущество можно рассмотреть и недостатки: мы каждый раз создаем новый объект в памяти, что дает на нее дополнительную нагрузку.

Рассмотрим UML диаграмму нашей программы:

Декоратор реализуется достаточно просто и применяется для динамического изменения объектов, их модернизации. Декоратор можно распознать по конструкторам, которые принимают в параметрах объекты того же абстрактного типа или интерфейса, что и текущий класс. В Java этот паттерн широко используется в классах ввода/вывода.

Например, как мы уже отметили, все подклассы java.io.InputStream , OutputStream , Reader и Writer имеют конструктор, принимающий объекты этих же классов.

Источник

Understand decorator patterns from InputStream

Russian nesting dolls. I think you all know them. If the expressions in each set are different, then for example, if I want to see the smiling face now, I just need the big set and the small set all the way to the smiling face. I think the classic implementation of the decorator pattern is like the Russian nesting dolls, where each doll is independent of the other, but can be used in combination. Think about it. Doesn’t it look like. The various InputStream streams in the JDK are classic implementations of the decorator pattern.

The body of the

1. Daily use of InputStream

In daily work, it is necessary to read files, and I will analyze InputStream from the daily code of reading files.

Case #1- Do not use a bufferInputStream public static void main(String[] args) < try (FileInputStream fileInputStream = new FileInputStream("/Users/weiyunpeng/Documents/test.txt")) < StringBuilder content = new StringBuilder(); final byte[] bytes = new byte[10]; int offset; while((offset = fileInputStream.read(bytes)) ! = -1) < content.append(new String(bytes, 0, offset)); > System.out.println("Read the contents of the file :" + content); > catch(IOException e) < e.printStackTrace(); >> # case2- Use one with a bufferInputStream public static void main(String[] args) < try (FileInputStream fileInputStream = new FileInputStream("/Users/Documents/test.txt"); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) < StringBuilder content = new StringBuilder(); final byte[] bytes = new byte[10]; int offset; while((offset = bufferedInputStream.read(bytes)) ! = -1) < content.append(new String(bytes, 0, offset)); > System.out.println("Read the contents of the file :" + content); > catch(IOException e) < e.printStackTrace(); >>Copy the code

The above code simply reads the test.txt file and prints its contents to the console. There is little difference between case 1 and case 2, and the output is the same. Case 2 uses FileInputStream and BufferedInputStream, and FileInputStream is passed as an argument to the constructor of BufferedInputStream. This allows you to use a feature unique to BufferedInputStream, the byte buffer. Let me draw a picture to illustrate the difference.

This is a hierarchy diagram without using a BufferedInputStream stream

This is the hierarchy diagram read after using the BufferedInputStream stream

2. Think — why does the JDK add different «features» to different streams by composing them

Suppose we had this requirement to add some special functionality to a class, how would we design it? Normally, we might inherit this class and override the methods in the parent class to add special functionality. Demands that is possible, but when the need is A variety of special features, the child will be more and more, will be more and more, it is difficult to maintain, this way will exist complex index level of inheritance, and various special function will not be able to free flexible combination between (A special function cannot at the same time there is B special features, unless again inheritance overriding methods), very troublesome.

Therefore, this problem can be avoided by composition, which is how the CLASSES related to input and output streams are implemented in the JDK. The principle is simple. There is a parent class (interface) InputStream, MyInputStream (read one byte at a time) and MyDecoratorInputStream (read a byte array as a buffer), each with different read stream properties. Let’s do it in code. The following abstract superclass, the read, write, foo method, has a basic implementation.

// Abstract superclass public abstract class InputStream < public void read(a)< System.out.println("InputStream default implementation"); > public void write(a)< System.out.println("InputStream default implementation"); > public void foo(a)< System.out.println("InputStream default implementation"); >>Copy the code

There are two subclasses, MyInputStream and MyDecoratorInputStream. MyDecoratorInputStream enhances the original read, write methods. When you need to use a reinforced method, you simply inject an instance of MyInputStream into an instance of MyDecoratorInputStream through the constructor, and then call the method of MyDecoratorInputStream. You can implement enhanced methods on top of the base functionality, the results of which are posted below.

// Subclass overrides all methods in the parent class public class MyInputStream extends InputStream < @Override public void read(a)< System.out.println("MyInputStream by default"); > @Override public void write(a)< System.out.println("MyInputStream by default"); > @Override public void foo(a)< System.out.println("MyInputStream by default"); >>// Decorator class -- Inherits the InputStream abstract class. Foo is not overridden public class MyDecoratorInputStream extends InputStream< private InputStream inputStream; public MyDecoratorInputStream(InputStream inputStream)< this.inputStream = inputStream; > @Override public void read(a)< / / enhancement System.out.println("MyDecoratorInputStream read"); // Original call inputStream.read(); > @Override public void write(a) < inputStream.write(); >I won't override the foo method > / / call public class TestCode < public static void main(String[] args) < MyInputStream myInputStream = new MyInputStream(); MyDecoratorInputStream myDecoratorInputStream = newMyDecoratorInputStream(myInputStream); myDecoratorInputStream.read(); myDecoratorInputStream.write(); myDecoratorInputStream.foo(); >>Copy the code

Finally, if my enhancement class only needs to be enhanced for one method in the parent class, then the other methods in the parent class need to be implemented again. Suppose MyInputStream only needs to be enhanced for the read method in InputStream. But MyInputStream still needs to implement the write method, even though the method body simply calls super.write(); So how does the JDK solve this problem? If you look at the code below, BufferedInputStream implements the FilterInputStream class, not InputStream.

public class BufferedInputStream extends FilterInputStream Copy the code

If you look at the FilterInputStream class, it provides some basic functionality for streams, so if there are other special features, the enhanced class simply inherits FilterInputStream and overwrites the methods that need to be enhanced. This eliminates the need to worry about unenhanced methods.

conclusion

Although the combination method is flexible to assemble various special functions, the multi-layer decoration also introduces complexity. List the pros and cons of use scenarios and decorator patterns.

Advantages: The decorator class and the decorator class can develop independently without coupling with each other. The decorator pattern is an inherited replacement pattern, and the decorator pattern can dynamically extend the functionality of an implementation class. Disadvantages: multi-layer decoration is more complex.

Seek wave concern, public number: Java Jinjintianlu

Источник

Читайте также:  Python для финансового директора
Оцените статью