Шаблон Proxy — это шаблон проектирования в разработке программного обеспечения, который включает создание объекта-заполнителя, который заменяет реальный объект. Объект Proxy действует как посредник между клиентом и реальным объектом, позволяя клиенту взаимодействовать с Proxy, как если бы это был реальный объект.

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

Существует несколько типов шаблонов прокси, включая удаленный прокси, виртуальный прокси, защитный прокси и интеллектуальный прокси.

давайте кратко рассмотрим их все:

как и в моей предыдущей статье, для меня очень важно убедиться, что информация доступна как можно большему количеству читателей. По этой причине я решил не использовать в этой статье какой-либо фреймворк JavaScript.

Шаблон виртуального прокси

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

// Real object
class Image {
  constructor(url) {
    this.url = url;
    this.loadImage();
  }
  loadImage() {
    console.log(`Loading image from ${this.url}`);
  }
  displayImage() {
    console.log(`Displaying image from ${this.url}`);
  }
}

// Proxy object
class ProxyImage {
  constructor(url) {
    this.url = url;
  }
  displayImage() {
    if (!this.image) {
      this.image = new Image(this.url);
    }
    this.image.displayImage();
  }
}

const imageURL = 'https://www.codethen.pizza/image.jpg'

// Usage
const realImage = new Image(imageURL);
realImage.displayImage(); 
/* 
Output: 
Loading image from https://www.codethen.pizza/image.jpg 
Displaying image from https://www.codethen.pizza/image.jpg
*/

const proxyImage = new ProxyImage(imageURL);
proxyImage.displayImage(); 
/*
Output: 
Loading image from https://www.codethen.pizza/image.jpg 
Displaying image from https://www.codethen.pizza/image.jpg
*/

// But the real object is created only when needed

В этом примере у нас есть объект Real Image, который загружает и отображает изображение. У нас также есть объект Proxy Image, который выступает в качестве заполнителя для реального изображения. Когда метод displayImage вызывается для прокси-изображения, он проверяет, было ли уже создано реальное изображение. Если нет, он создает настоящий объект Image, а затем вызывает для него метод displayImage. Таким образом, реальное изображение создается только при необходимости, что может быть полезно в ситуациях, когда загрузка изображения требует больших затрат или времени.

Шаблон удаленного прокси

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

// Real object
class UserService {
  getUser(id) {
    console.log(`Fetching user with id ${id} from remote server...`);
    // Assume this makes a network request to fetch the user data
    return { id: id, name: 'John Doe', email: '[email protected]' };
  }
}

// Proxy object
class UserServiceProxy {
  constructor() {
    this.userService = new UserService();
    this.cache = {};
  }
  getUser(id) {
    if (this.cache[id]) {
      console.log(`Fetching user with id ${id} from cache...`);
      return this.cache[id];
    } else {
      console.log(`Fetching user with id ${id} from remote server...`);
      const user = this.userService.getUser(id);
      this.cache[id] = user;
      return user;
    }
  }
}

// Usage
const userService = new UserServiceProxy();
console.log(userService.getUser(1)); 
/* Output: 
Fetching user with id 1 from remote server... 
{ id: 1, name: 'John Doe', email: '[email protected]' } 
*/

console.log(userService.getUser(1)); 
/* Output: 
Fetching user with id 1 from cache... 
{ id: 1, name: 'John Doe', email: '[email protected]' }
*/

/* 
The proxy caches previously fetched users to avoid
unnecessary network requests
*/

Видеть? у нас есть объект Real User Service, который извлекает пользовательские данные с удаленного сервера. У нас также есть объект Proxy User Service, который действует как посредник между клиентом и реальным User Service. Когда метод getUser вызывается в прокси-службе пользователей, он проверяет, были ли данные пользователя кэшированы. Если это так, он возвращает кэшированные пользовательские данные. В противном случае он вызывает метод getUser для реальной службы пользователя, кэширует результат, а затем возвращает его.

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

Шаблон прокси-сервера защиты

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

// Real object
class BankAccount {
  constructor(balance = 0) {
    this.balance = balance;
  }
  deposit(amount) {
    this.balance += amount;
    console.log(`Deposited ${amount} dollars. New balance is ${this.balance} dollars.`);
  }
  withdraw(amount) {
    if (this.balance >= amount) {
      this.balance -= amount;
      console.log(`Withdrew ${amount} dollars. New balance is ${this.balance} dollars.`);
    } else {
      console.log(`Error: Insufficient funds. Balance is ${this.balance} dollars.`);
    }
  }
}

// Proxy object
class BankAccountProtectionProxy {
  constructor(balance, secretCode) {
    this.bankAccount = new BankAccount(balance);
    this.secretCode = secretCode;
  }
  deposit(amount) {
    this.bankAccount.deposit(amount);
  }
  withdraw(amount, secretCode) {
    if (secretCode === this.secretCode) {
      this.bankAccount.withdraw(amount);
    } else {
      console.log(`Error: Unauthorized access. Withdrawal not allowed.`);
    }
  }
}

// Usage
const bankAccount = new BankAccountProtectionProxy(1000, '1234');
bankAccount.deposit(500); 
/* 
Output: 
Deposited 500 dollars. New balance is 1500 dollars. 
*/

bankAccount.withdraw(200, '1234'); 
/* 
Output: 
Withdrew 200 dollars. New balance is 1300 dollars.
*/

bankAccount.withdraw(500, '5678'); 
/* 
Output: Error: Unauthorized access. Withdrawal not allowed.
*/

/* 
The proxy object restricts access to the real object by
requiring a secret code for withdrawals 
/*

Шаблон смарт-прокси

Смарт-прокси — это тип шаблона прокси, который используется для выполнения дополнительных действий при доступе к объекту. Smart Proxy также известен как «Intelligent Proxy», потому что он может добавить интеллекта к доступу к объекту.

function heavyFunction() {
  // imagine this function takes a long time to execute
  console.log("Executing heavyFunction...");
}

function smartProxy() {
  let count = 0;

  return function() {
    console.log(`heavyFunction has been called ${++count} times`);
    return heavyFunction.apply(this, arguments);
  }
}

const smartHeavyFunction = smartProxy();

smartHeavyFunction();
/* 
logs "heavyFunction has been called 1 times"
logs "Executing heavyFunction..."
*/

smartHeavyFunction();
/* 
logs "heavyFunction has been called 2 times"
logs "Executing heavyFunction..."
*/

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

Затем мы создаем новую переменную smartHeavyFunction, которая содержит возвращенную функцию из smartProxy. Когда мы вызываем smartHeavyFunction, он регистрирует количество вызовов heavyFunction, а затем вызывает исходную функцию.

Это всего лишь один пример того, как шаблон Smart Proxy можно использовать для добавления функциональности объекту, не изменяя его напрямую.

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

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

Сводка

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

Подробнее о шаблонах:

Подробнее: