Коротко о шаблоне адаптера

Версия JavaScript

Традиционно структурные паттерны описываются как то, что помогает упростить процесс композиции объекта. Его цель - помочь определить взаимосвязь между различными объектами, гарантируя, что если что-то изменится в системе, это не заставит всю систему измениться вместе с этим.

Начнем с выкройки переходника.

Шаблон адаптера

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

Но процесс перехода займет много времени, и вы не хотите, чтобы ваш код был слишком переплетен со старым кодом. Что вы делаете?

В подобных ситуациях вам пригодится шаблон адаптера.

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

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

Итак, как именно написать шаблон адаптера на JavaScript?

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

Следует отметить, что адаптер имеет только одно направление зависимости. Адаптер потребляет только то, что он предназначен для перевода. В большинстве случаев это устаревший API или сторонние библиотеки. Он не делает ничего, кроме установления связи между двумя сторонами приложения. Он не должен содержать никакой бизнес-логики.

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

Давайте сначала посмотрим на экспортированную class версию.

//your original cart data service
 //this is just a hypothetical name
 import { v4 as cart } from "cartServiceV4"
 ​
 class CartServiceAdapter {
     getCart(){
         //some cart code 
         //using data from the imported cart service
         //can transform the original output to suit your needs
         return cart;
     }
     
     removeItemFromCart(){
         //some cart code
         return cart;
     }
     
     //etc
 }
 ​
 export default new CartService();

import добавляет то, что вы хотите перевести. export делает ваш CartServiceAdapter доступным для использования всем, кто его импортирует.

Когда вам нужны cart данные, вам просто нужно импортировать адаптер в свой код, используя import.

import cart from "./utils/CartServiceAdapter"
 ​
 console.log(cart.getCart());

Как этот гипотетический код выглядит в виде диаграммы:

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

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

//original cart data that cannot be changed at source
 //let's pretend this is from a mysterious data service
 //we have no direct access to this
 var cart = [
   {item: "vintage clock", sku: 9284, value: 15.99},
   {item: "motivate carts", sku: 9232, value: 19.99}
 ]
 ​
 //old interface pulls in data from mysterious data service
 //we're not allowed to touch this
 function Cart(){
     return this.cart;
 }
 ​
 //our adapter code to translate the data
 //prevents us from consuming the Cart() directly
 function CartAdapter(){  
   var originalCart = Cart();  
   var adapterCart = originalCart.map(function(obj){
     return {
       item: obj.item,
       productId: obj.sku,
       price:obj.value
       
     }
   });
   
   return adapterCart;
 }
 ​
 //now we can use the result of the CartAdapter() however we want
 //changes in Cart() will result in only a change in the adapter
 //the rest of the code remains unimpacted once the adapter is fixed
 console.log(CartAdapter());

В приведенном выше примере мы делаем вид, что формат данных cart не может быть изменен, что он загружается в приложение с помощью функции Cart(). Cart() можно рассматривать как старый интерфейс, к которому мы не хотим прикасаться. CartAdapter() как промежуточный фрагмент кода, который не позволяет нам использовать Cart() напрямую. Когда что-то меняется в Cart(), нам нужно изменить только CartAdapter(), а не семейство функций, которые могут напрямую потреблять данные из Cart().

Шаблон адаптера пригодится, когда вы работаете на нескольких платформах, источниках данных, библиотеках и фреймворках. Он заполняет пробелы, вызванные различными требованиями и различиями в средах. Контекст исходного кода может иметь смысл, но не в том случае, когда его нужно использовать. Шаблон адаптера делает это возможным, соединяя различные источники вместе.