наш блог

Доходчивый пример использования паттерна Adapter на JavaScript

Adapter – это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе.

Этот шаблон крайне легко понять, так как реальный мир переполнен разнообразными адаптерами. Скажем, когда заказанный в Китае смартфон доставляется из Поднебесной в комплекте с зарядным устройством, которое не подходит к стандартным разъемам отечественных розеток, то новоиспеченный владелец заграничного гаджета попросту отправляется в магазин за переходником. Иначе говоря, адаптером.

Теперь рассмотрим на примере. Предположим, что есть некий проект. А в нем присутствует часть кода, отвечающая за конвертацию картинок из одного формата в другой. Для этого используется сторонняя библиотека с таким кодом:

imageConverter.js

export class ImageConverter {



static convert(image, newFormat) {

  // Какая-нибудь сложная логика...

  console.log(`Image converted to ${newFormat}!`);

  return image

}

}

Предположим, что вышеупомянутая библиотека успешно работает в проекте. И используется в разных местах. Но в один прекрасный день появляется необходимость подключить еще одну библиотеку. Она должна будет дополнять изображения meta-записями. При этом программист получает ссылку на библиотеку с таким кодом:

imageMeta.js

export class ImageMeta {



constructor(imageConverter) {

  this.converter = imageConverter

}



addMetaToJPG(image) {

  const convertedImage = this.converter.convertToJPG(image);

  convertedImage.meta = 'some meta for jpg'; // Очень сложная логика добавления meta для картинок с расширением jpg :)

  return convertedImage;

}



addMetaToPNG(image) {

  const convertedImage = new this.converter.convertToPNG(image);

  convertedImage.meta = 'some meta for png'; // Очень сложная логика добавления meta для картинок с расширением png :)

  return convertedImage;

}

}

Как видно, класс ImageMeta принимает в аргументы конструктора конвертер изображений. Дело в том, что сама библиотека не умеет преобразовывать картинки. При этом для добавления meta-записей к иллюстрациям тех или иных форматов задействует разную логику. Именно поэтому разработчики библиотеки используют конвертер. Так как они должны быть полностью уверены, что все картинки будут иметь подходящий формат.

index.js

import {ImageMeta} from './imageMeta';

import {ImageConverter} from './imageConverter';



// Предположим, что эта наша катринка

const image = {

path: 'https://web-artcraft.com/someimage.gif'

};



const imageMeta = new ImageMeta(ImageConverter);



const result = imageMeta.addMetaToJPG(image);

Здесь и возникает проблема. Методы addMetaToJPG() и addMetaToPNG() библиотеки ImageMeta ожидают, что у конвертера будут методы convertToJPG и convertToPNG соответственно. А у конвертера, который используется в проекте, есть только метод convert(). Изменение или дописывание методов уже существующей библиотеки не считается хорошей практикой. Для решения проблемы рекомендуется использовать паттерн Adapter.

Напишем класс, реализующий методы, которые будут необходимы библиотеке ImageMeta. А внутри них задействуем уже существующий конвертер.

imageConverterAdapter.js

import { ImageConverter } from './imageConverter';



export class ImageConverterAdapter {



static convertToJPG(image) {

  ImageConverter.convert(image, 'jpg')

}



static convertToPNG(image) {

  ImageConverter.convert(image, 'png')

}



}

Теперь в index.js

import {ImageMeta} from './imageMeta';

import {ImageConverterAdapter} from './adapter';



const image = {

path: 'https://web-artcraft.com/someimage.gif'

};



const imageWithMeta = new ImageMeta(ImageConverterAdapter);



const result = imageWithMeta.addMetaToJPG(image);

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

Понятно, что пример надуманный. Однако, как показывает опыт, он в достаточной степени раскрывает идею применения паттерна Adapter.

Юрий Кизилов, компания Craft Group