JavaRush /جاوا بلاگ /Random-UR /Паттерн проектирования Адаптер

Паттерн проектирования Адаптер

گروپ میں شائع ہوا۔
Hello! Сегодня мы затронем важную новую тему — паттерны, or по-другому — шаблоны проектирования. What же такое паттерны? Думаю, тебе известно выражение «не надо изобретать велосипед». В программировании, How и во многих других сферах, есть большое количество типовых ситуаций. Для каждой из них в процессе развития программирования создавались готовые работающие решения. Это и есть шаблоны проектирования. Условно говоря, паттерн — это некий пример, который предлагает решение ситуации вида: «если в вашей программе нужно сделать то-то, How это лучше всего сделать». Паттернов очень много, им посвящена отличная книга «Изучаем шаблоны проектирования», с которой обязательно нужно ознакомиться. Паттерн проектирования «Адаптер» - 2Если говорить максимально кратко, паттерн состоит из распространенной проблемы и ее решения, которое уже можно считать неким standard. В сегодняшней лекции мы познакомимся с одним из таких паттернов под названием «Адаптер». Название у него говорящее, и ты не раз встречался с адаптерами в реальной жизни. Один из самых распространенных адаптеров — кардридеры, которыми снабжены множество компьютеров и ноутбуков. Паттерн проектирования «Адаптер» - 3Представь, что у нас есть Howая-то карта памяти. В чем состоит проблема? В том, что она не умеет взаимодействовать с компьютером. У них нет общего интерфейса. У компьютера есть разъем USB, но карту памяти в него не вставить. Карту невозможно вставить в компьютер, из-за чего мы не сможем сохранить наши фотографии, видео и другие данные. Кардридер является адаптером, решающим данную проблему. Ведь у него есть USB-кабель! В отличие от самой карты, кардридер можно вставить в компьютер. У них с компьютером есть общий интерфейс — USB. Давай посмотрим, How это будет выглядеть на примере:

public interface USB {

   void connectWithUsbCable();
}
Это наш интерфейс USB с единственным методом — вставить USB-кабель:

public class MemoryCard {

   public void insert() {
       System.out.println("Карта памяти успешно вставлена!");
   }

   public void copyData() {
       System.out.println("Данные скопированы на компьютер!");
   }
}
Это наш класс, реализующий карту памяти. В нем уже есть 2 нужных нам метода, но вот беда: интерфейс USB он не реализует. Карту нельзя вставить в USB-разъем.

public class CardReader implements USB {

   private MemoryCard memoryCard;

   public CardReader(MemoryCard memoryCard) {
       this.memoryCard = memoryCard;
   }

   @Override
   public void connectWithUsbCable() {
       this.memoryCard.insert();
       this.memoryCard.copyData();
   }
}
А вот и наш адаптер! What же делает класс CardReader и почему, собственно, он является адаптером? Все просто. Адаптируемый класс (карта памяти) становится одним из полей адаптера. Это логично, ведь в реальной жизни мы тоже вставляем карту внутрь кардридера, и она тоже становится его частью. В отличие от карты памяти, у адаптера есть общий интерфейс с компьютером. У него есть USB-кабель, то есть он может соединяться с другими устройствами по USB. Поэтому в программе наш класс CardReader реализует интерфейс USB. Но что же происходит внутри этого метода? А там происходит ровно то, что нам нужно! Адаптер делегирует выполнение работы нашей карте памяти. Ведь сам-то адаптер ничего не делает, Howого-то самостоятельного функционала у кардридера нет. Его задача — только связать компьютер и карту памяти, чтобы карта могла сделать свою работу и скопировать файлы! Наш адаптер позволяет ей сделать это, предоставив свой интерфейс (метод connectWithUsbCable()) для «нужд» карты памяти. Давай создадим Howую-то программу-клиент, которая будет имитировать человека, желающего скопировать данные с карты памяти:

public class Main {
  
   public static void main(String[] args) {

       USB cardReader = new CardReader(new MemoryCard());
       cardReader.connectWithUsbCable();

   }
}
What же у нас в результате получилось? Вывод в консоль:

Карта памяти успешно вставлена!
Данные скопированы на компьютер!
Отлично, наша задача успешно выполнена! Вот несколько дополнительных ссылок с информацией о паттерне Адаптер:

Абстрактные классы Reader и Writer

Теперь мы вернемся к нашему любимому занятию: выучим парочку новых классов для работы со вводом и выводом :) Сколько мы их уже выучor, интересно? Сегодня речь пойдет о классах Reader и Writer. Почему именно о них? Потому что это будет в тему нашему предыдущему разделу — адаптерам. Давай рассмотрим их подробнее. Начнем с Reader’a. Reader — это абстрактный класс, поэтому явно создавать его an objectы у нас не получится. Но на самом деле ты с ним уже знаком! Ведь хорошо знакомые тебе классы BufferedReader и InputStreamReader являются его наследниками :)

public class BufferedReader extends Reader {}

public class InputStreamReader extends Reader {}
Так вот, класс InputStreamReader — это классический адаптер. Как ты, наверное, помнишь, мы можем передать в его конструктор an object InputStream. Чаще всего мы для этого используем переменную System.in:

public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
What же делает InputStreamReader? Как и всякий адаптер, он преобразует один интерфейс к другому. В данном случае — интерфейс InputStream’a к интерфейсу Reader’a. Изначально у нас был класс InputStream. Он неплохо работает, но с его помощью можно читать только отдельные byteы. Кроме того, у нас есть абстрактный класс Reader. У него есть отличный и очень нужный нам функционал — он умеет читать символы! Нам такая возможность, конечно, очень нужна. Но здесь мы сталкиваемся с классической проблемой, которую обычно решают адаптеры — несовместимость интерфейсов. В чем же она проявляется? Давай заглянем прямо в documentацию Oracle. Вот методы класса InputStream. Паттерн проектирования «Адаптер» - 4Совокупность методов — это и есть интерфейс. Как видишь, метод read() у этого класса есть (даже в нескольких вариантах), но читать он может только byteы: or отдельные byteы, or несколько byte с использованием буфера. Нам такой вариант не подходит — мы хотим читать символы. Нужный нам функционал уже реализован в абстрактном классе Reader. Это тоже можно увидеть в documentации. Паттерн проектирования «Адаптер» - 5Однако интерфейсы InputStream'a и Reader'a несовместимы! Как видишь, во всех реализациях метода read() у них отличаются и передаваемые параметры, и возвращаемые значения. И именно здесь нам понадобится InputStreamReader! Он выступит Адаптером между нашими классами. Как и в примере с кардридером, который мы рассмотрели выше, мы передаем an object «адаптируемого» класса «внутрь», то есть в конструктор класса-адаптера. В прошлом примере мы передавали an object MemoryCard внутрь CardReader. А теперь передаем an object InputStream в конструктор InputStreamReader! В качестве InputStream мы используем уже ставшую привычной переменную System.in:

public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
И действительно: заглянув в documentацию InputStreamReader'a мы увидим, что «адаптация» прошла успешно :) Теперь в нашем распоряжении есть методы, которые позволяют нам читать символы. Паттерн проектирования «Адаптер» - 6И хотя изначально наш an object System.in (поток, привязанный к клавиатуре) не позволял этого делать, создав паттерн Адаптер создатели языка решor эту проблему. У абстрактного класса Reader, How и у большинства I/O-классов, есть брат-близнец — Writer. Он имеет тот же большой плюс, что и Reader — предоставляет удобный интерфейс для работы с символами. С выходными потоками проблема и ее решение выглядят так же, How и в случае со входными. Есть класс OutputStream, который умеет записывать только byteы; есть абстрактный класс Writer, который умеет работать с символами, и есть два несовместимых интерфейса. Эту проблему вновь успешно решает паттерн Адаптер. При помощи класса OutputStreamWriter мы легко «адаптируем» два интерфейса классов Writer и OutputStream друг другу. И, получив byteовый поток OutputStream в конструктор, с помощью OutputStreamWriter мы, тем не менее, можем записывать символы, а не byteы!

import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException {

       OutputStreamWriter streamWriter = new OutputStreamWriter(new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt"));
       streamWriter.write(32144);
       streamWriter.close();
   }
}
Мы записали в наш файл символ с codeом 32144 — 綐, таким образом избавившись от необходимости работать с byteами :) На этом на сегодня все, до встречи на следующих лекциях! :)
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION