JavaRush /Java блог /Random UA /Паттерн проектування Адаптер

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

Стаття з групи Random UA
Вітання! Сьогодні ми торкнемося важливої ​​нової теми — патерни, або по-іншому — шаблонів проектування . Що ж таке патерни? Думаю, тобі відомий вислів «не треба винаходити велосипед». У програмуванні, як і багатьох інших сферах, є велика кількість типових ситуацій. Для кожної їх у процесі розвитку програмування створювалися готові діючі рішення. Це і є шаблони проектування. Умовно кажучи, патерн — це приклад, який пропонує вирішення ситуації виду: «якщо у вашій програмі потрібно зробити те, як це найкраще зробити». Паттернов дуже багато, їм присвячено чудову книгу «Вивчаємо шаблони проектування», з якою обов'язково потрібно ознайомитися. Паттерн проектування "Адаптер" - 2Якщо говорити максимально коротко, патерн складається з поширеної проблеми та її вирішення, яке вже можна вважати стандартом. У сьогоднішній лекції ми познайомимося з одним із таких патернів під назвою «Адаптер». Назва в нього говорить, і ти неодноразово зустрічався з адаптерами в реальному житті. Один із найпоширеніших адаптерів - кардрідери, якими забезпечені безліч комп'ютерів та ноутбуків. Паттерн проектування "Адаптер" - 3Уяви, що ми маємо якусь карту пам'яті. У чому проблема? У цьому, що вона вміє взаємодіяти з комп'ютером. Вони не мають спільного інтерфейсу. Комп'ютер має роз'єм USB, але карту пам'яті в нього не вставити. Карту неможливо вставити в комп'ютер, через що ми не зможемо зберегти наші фотографії, відео та інші дані. Кардрідер є адаптером, який вирішує цю проблему. Адже він має USB-кабель! На відміну від самої карти кардрідер можна вставити в комп'ютер. У них з комп'ютером є спільний інтерфейс USB. Давай подивимося, як це виглядатиме на прикладі:
public interface USB {

   void connectWithUsbCable();
}
Це наш інтерфейс USB з єдиним методом – вставити USB-кабель:
public class MemoryCard {

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

   public void copyData() {
       System.out.println("Данные скопированы на компьютер!");
   }
}
Це наш клас, який реалізує картку пам'яті. У ньому вже є два необхідні нам способу, але ось біда: інтерфейс 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();
   }
}
А ось і наш адаптер! Що робить класCardReader і чому, власне, він є адаптером? Все просто. Адаптований клас (карта пам'яті) стає одним із полів адаптера. Це логічно, адже в реальному житті ми теж вставляємо карту всередину кардрідера, і вона також стає його частиною. На відміну від карти пам'яті, адаптер має спільний інтерфейс з комп'ютером. Він має USB-кабель, тобто він може з'єднуватися з іншими пристроями по USB. Тому у програмі наш класCardReaderреалізує інтерфейс USB. Але що відбувається всередині цього методу? А там відбувається те, що нам потрібно! Адаптер делегує виконання нашої карти пам'яті. Адже сам адаптер нічого не робить, якогось самостійного функціоналу у кардрідера немає. Його завдання – лише зв'язати комп'ютер та карту пам'яті, щоб картка могла зробити свою роботу та скопіювати файли! Наш адаптер дозволяє їй зробити це, надавши свій інтерфейс (метод connectWithUsbCable()) для «потрібних» карт пам'яті. Давай створимо якусь програму-клієнт, яка імітуватиме людину, яка бажає скопіювати дані з картки пам'яті:
public class Main {

   public static void main(String[] args) {

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

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

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

Тепер ми повернемося до нашого улюбленого заняття: вивчимо кілька нових класів для роботи з введенням і висновком :) Скільки ми їх вже вивчабо, цікаво? Сьогодні мова піде про класи Readerта Writer. Чому саме про них? Тому що це буде у тему нашого попереднього розділу — адаптерів. Давай розглянемо їх докладніше. Почнемо з Reader'a. Reader- Це абстрактний клас, тому явно створювати його об'єкти у нас не вийде. Але насправді ти з ним уже знайомий! Адже добре знайомі тобі класи BufferedReaderі InputStreamReaderє його спадкоємцями:)
public class BufferedReader extends Reader {}

public class InputStreamReader extends Reader {}
Так ось, клас InputStreamReader– це класичний адаптер . Як ти, напевно, пам'ятаєш, ми можемо передати до його конструктора об'єкт InputStream. Найчастіше ми для цього використовуємо змінну System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
Що ж робить InputStreamReader? Як і будь-який адаптер, він перетворює один інтерфейс до іншого. У разі — інтерфейс InputStream'a до інтерфейсу Reader'a. Спочатку у нас був клас InputStream. Він непогано працює, але з його допомогою можна читати лише окремі байти. Крім того, у нас є абстрактний клас Reader. Він має чудовий і дуже потрібний нам функціонал — він вміє читати символи! Нам така можливість, звісно, ​​дуже потрібна. Але тут ми стикаємося із класичною проблемою, яку зазвичай вирішують адаптери – несумісність інтерфейсів. У чому вона проявляється? Давай заглянемо прямо до документації Oracle. Ось методи класу InputStream. Паттерн проектування "Адаптер" - 4Сукупність методів - це і є інтерфейс. Як бачиш, методread()у цього класу є (навіть у кількох варіантах), але читати може лише байти: або окремі байти, або кілька байт з використанням буфера. Нам такий варіант не підходить – ми хочемо читати символи. Потрібний нам функціонал уже реалізовано в абстрактному класіReader . Це також можна побачити в документації. Паттерн проектування "Адаптер" - 5Однак інтерфейси InputStream'a та Reader'a несумісні! Як бачиш, у всіх реалізаціях методу read()у них відрізняються і параметри, що передаються, і значення, що повертаються. І саме тут нам знадобиться InputStreamReader! Він виступить Адаптер між нашими класами. Як і в прикладі з кардрідером, який ми розглянули вище, ми передаємо об'єкт «адаптованого» класу «всередину», тобто конструктор класу-адаптера. Минулого прикладу ми передавали об'єктMemoryCardвсередину CardReader. А тепер передаємо об'єкт InputStreamу конструктор InputStreamReader! Як InputStreamми використовуємо змінну , що вже стала звичною System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
І справді: зазирнувши в документацію InputStreamReaderми побачимо, що «адаптація» пройшла успішно :) Тепер у нашому розпорядженні є методи, які дозволяють нам читати символи. Паттерн проектування "Адаптер" - 6І хоча споконвічно наш об'єкт System.in(потік, прив'язаний до клавіатури) не дозволяв цього робити, створивши патерн Адаптер творці мови вирішабо цю проблему. Абстрактний клас Reader, як і більшість I/O-класів, має брат-близнюка — Writer. Він має той же великий плюс, що і Readerнадає зручний інтерфейс для роботи з символами. З вихідними потоками проблема та її вирішення виглядають так само, як і у випадку з вхідними. Є клас OutputStream, який вміє записувати лише байти; є абстрактний класWriter, що вміє працювати з символами, і є два несумісні інтерфейси. Цю проблему знову успішно вирішує патерн Адаптер. За допомогою класу OutputStreamWriterми легко «адаптуємо» два інтерфейси класів Writerта OutputStreamодин одному. І, отримавши байтовий потік OutputStreamу конструктор, за допомогою OutputStreamWriterми можемо записувати символи, а не байти!
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();
   }
}
Ми записали в наш файл символ з кодом 32144 — 綐, таким чином позбавившись необхідності працювати з байтами :) На цьому на сьогодні все, до зустрічі на наступних лекціях! :)
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ