Вступление
Шаблон проектирования Прокси – это шаблон проектирования, принадлежащий набору структурных шаблонов . Структурные шаблоны-это категория шаблонов проектирования, используемых для упрощения проектирования программы на ее структурном уровне.
Как следует из его названия, шаблон прокси означает использование прокси для какой-либо другой сущности. Другими словами, прокси-сервер используется в качестве посредника перед существующим объектом или вокруг него. Это может быть использовано, например, когда фактический объект очень ресурсоемкий или когда существуют определенные условия, которые необходимо проверить перед использованием фактического объекта. Прокси-сервер также может быть полезен, если мы хотим ограничить доступ или функциональность объекта.
В этой статье мы опишем шаблон прокси-сервера и покажем несколько примеров, в которых его можно использовать.
Идея, лежащая в основе Прокси
Прокси-сервер используется для инкапсуляции функциональных возможностей другого объекта или системы. Рассмотрим , например, вызов удаленного метода , который является способом вызова методов на другой машине. В Java это выполняется с помощью удаленного прокси-сервера , который по сути является объектом, обеспечивающим локальное представление другого удаленного объекта. Вызов метода с другой машины становится возможным просто путем вызова метода прокси-объекта.
Каждый прокси реализован таким образом, что он предлагает клиенту точно такой же интерфейс, как и реальный объект. Это означает, что клиент фактически не замечает разницы при использовании прокси-объекта.
Существует несколько типов прокси-объектов. Как, вероятно, можно сделать вывод из предыдущего примера, удаленные прокси-серверы используются для доступа к некоторым удаленным объектам или ресурсам. Помимо удаленных прокси-серверов, существуют также виртуальные прокси-серверы и прокси-серверы защиты . Давайте кратко опишем каждый из них для лучшего понимания.
Удаленные Прокси-серверы
Удаленные прокси предоставляют локальное представление другого удаленного объекта или ресурса. Удаленные прокси-серверы отвечают не только за представление, но и за некоторые работы по техническому обслуживанию. Такая работа может включать подключение к удаленной машине и поддержание соединения, кодирование и декодирование символов, полученных с помощью сетевого трафика, синтаксический анализ и т.д.
Виртуальные Прокси
Виртуальные прокси переносят дорогие объекты и загружают их по требованию. Иногда нам не сразу нужны все функции, которые предлагает объект, особенно если это требует много памяти/времени. Вызов объектов только при необходимости может значительно повысить производительность, как мы увидим в примере ниже.
Прокси-серверы Защиты
Прокси-серверы защиты используются для проверки определенных условий. Некоторым объектам или ресурсам может потребоваться соответствующая авторизация для доступа к ним, поэтому использование прокси-сервера является одним из способов проверки таких условий. С помощью прокси-серверов защиты мы также получаем гибкость, связанную со множеством вариантов контроля доступа.
Например, если мы пытаемся предоставить доступ к ресурсу операционной системы, обычно существует несколько категорий пользователей. У нас может быть пользователь, которому запрещено просматривать или редактировать ресурс, пользователь, который может делать с ресурсом все, что пожелает, и т. Д.
Использование прокси-серверов в качестве оболочек для таких ресурсов-отличный способ реализации индивидуального контроля доступа.
Реализация
Пример виртуального Прокси-Сервера
Одним из примеров виртуального прокси-сервера является загрузка изображений. Давайте представим, что мы создаем файловый менеджер. Как и любой другой файловый менеджер, этот должен иметь возможность отображать изображения в папке, которую пользователь решит открыть.
Если мы предположим , что существует класс ImageViewer
, отвечающий за загрузку и отображение изображений, мы могли бы реализовать наш файловый менеджер, используя этот класс напрямую. Такой подход кажется логичным и прямолинейным, но в нем есть тонкая проблема.
Если мы реализуем файловый менеджер, как описано выше, мы будем загружать изображения каждый раз, когда они появляются в папке. Если пользователь хочет видеть только имя или размер изображения, такой подход все равно загрузит все изображение в память. Поскольку загрузка и отображение изображений являются дорогостоящими операциями, это может привести к проблемам с производительностью.
Лучшим решением было бы отображать изображения только тогда, когда это действительно необходимо . В этом смысле мы можем использовать прокси-сервер для переноса существующего средства просмотра изображений
объекта. Таким образом, фактический просмотрщик изображений будет вызываться только тогда, когда изображение необходимо отрисовывать. Все остальные операции (такие как получение имени изображения, размера, даты создания и т.д.) Не требуют фактического изображения и поэтому могут быть получены с помощью гораздо более легкого прокси-объекта.
Давайте сначала создадим наш основной интерфейс:
interface ImageViewer { public void displayImage(); }
Далее мы реализуем средство просмотра конкретных изображений. Обратите внимание, что операции, выполняемые в этом классе, являются дорогостоящими:
public class ConcreteImageViewer implements ImageViewer { private Image image; public ConcreteImageViewer(String path) { // Costly operation this.image = Image.load(path); } @Override public void displayImage() { // Costly operation image.display(); } }
Теперь мы реализуем наш легкий прокси-сервер для просмотра изображений. Этот объект будет вызывать средство просмотра конкретных изображений только при необходимости, т. Е. когда клиент вызывает метод displayImage ()
. До тех пор изображения загружаться или обрабатываться не будут , что сделает нашу программу намного более эффективной.
public class ImageViewerProxy implements ImageViewer { private String path; private ImageViewer viewer; public ImageViewerProxy(String path) { this.path = path; } @Override public void displayImage() { this.viewer = new ConcreteImageViewer(this.path); this.viewer.displayImage(); } }
Наконец, мы напишем клиентскую часть нашей программы. В приведенном ниже коде мы создаем шесть различных средств просмотра изображений. Во-первых, три из них являются конкретными средствами просмотра изображений, которые автоматически загружают изображения при создании. Последние три изображения не загружают никаких изображений в память при создании.
Только с последней строки первый просмотрщик прокси начнет загрузку изображения. По сравнению с конкретными зрителями преимущества производительности очевидны:
public static void main(String[] args) { ImageViewer flowers = new ConcreteImageViewer("./photos/flowers.png"); ImageViewer trees = new ConcreteImageViewer("./photos/trees.png"); ImageViewer grass = new ConcreteImageViewer("./photos/grass.png"); ImageViewer sky = new ImageViewerProxy("./photos/sky.png"); ImageViewer sun = new ImageViewerProxy("./photos/sun.png"); ImageViewer clouds = new ImageViewerProxy("./photos/clouds.png"); sky.displayImage(); }
Еще одна вещь, которую мы могли бы сделать, это добавить null
-проверку в displayImage()
метод прокси-сервера Просмотра изображений
:
@Override public void displayImage() { if (this.viewer == null) { this.viewer = new ConcreteImageViewer(this.path); } this.viewer.displayImage(); }
Итак, если мы позвоним:
Git Essentials
Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!
ImageViewer sky = new ImageViewerProxy("./photos/sky.png"); sky.displayImage(); sky.displayImage();
Только один раз будет выполнен вызов нового средства просмотра конкретных изображений
. Это еще больше уменьшит объем памяти нашего приложения.
Примечание: Этот пример не содержит полностью компилируемый Java-код. Некоторые вызовы методов, такие как Image.load(путь к строке)
, являются вымышленными и написаны упрощенным способом в основном для иллюстрации.
Пример Прокси-сервера Защиты
В этом примере мы будем летать на космическом корабле. До этого нам нужно создать две вещи: Космический корабль
интерфейс и Пилот
модель:
interface Spaceship { public void fly(); }
public class Pilot { private String name; // Constructor, Getters, and Setters }
Теперь мы собираемся реализовать интерфейс Spaceship
и создать реальный класс spaceship:
public class MillenniumFalcon implements Spaceship { @Override public void fly() { System.out.println("Welcome, Han. The Millennium Falcon is starting up its engines!"); } }
Класс Тысячелетний Сокол
представляет собой конкретный космический корабль, который может быть использован нашим Пилотом
. Однако могут быть некоторые условия, которые мы хотели бы проверить, прежде чем позволить пилоту управлять космическим кораблем. Например, возможно, мы хотели бы узнать, есть ли у пилота соответствующий сертификат или он достаточно взрослый, чтобы летать. Чтобы проверить эти условия, мы можем использовать шаблон проектирования прокси-сервера.
В этом примере мы собираемся проверить, зовут ли пилота “Хан Соло”, поскольку он является законным владельцем корабля. Мы начинаем с реализации интерфейса Spaceship
, как и раньше.
Мы собираемся использовать Пилот
и Космический корабль
в качестве переменных нашего класса, поскольку мы можем получить от них всю необходимую информацию:
public class MillenniumFalconProxy implements Spaceship { private Pilot pilot; private Spaceship falcon; public MillenniumFalconProxy(Pilot pilot) { this.pilot = pilot; this.falcon = new MillenniumFalcon(); } @Override public void fly() { if (pilot.getName().equals("Han Solo")) { falcon.fly(); } else { System.out.printf("Sorry %s, only Han Solo can fly the Falcon!\n", pilotName); } } }
Затем клиентская часть программы может быть написана, как показано ниже. Если “Хан Соло” будет пилотом, “Соколу” будет разрешено летать. В противном случае ему не разрешат покинуть ангар:
public static void main(String[] args) { Spaceship falcon1 = new MillenniumFalconProxy(new Pilot("Han Solo")); falcon1.fly(); Spaceship falcon2 = new MillenniumFalconProxy(new Pilot("Jabba the Hutt")); falcon2.fly(); }
Выходные данные для вышеуказанных вызовов приведут к следующему:
Welcome, Han. The Millennium Falcon is starting up its engines! Sorry Jabba the Hutt, only Han Solo can fly the Falcon!
плюсы и минусы
Плюсы
- Безопасность : С помощью прокси-сервера можно проверить определенные условия при доступе к объекту и обеспечить контролируемое использование потенциально “опасных” классов и ресурсов.
- Производительность : Некоторые объекты могут быть очень требовательными с точки зрения памяти и времени выполнения. Используя прокси-сервер, мы можем обернуть такие объекты дорогостоящими операциями, чтобы они вызывались только тогда, когда это действительно необходимо, или избежать ненужного создания экземпляров.
Аферы
Производительность : Да, производительность также может быть недостатком прокси-шаблона. Как, вы могли бы спросить? Предположим, что прокси-объект используется для переноса объекта, существующего где-то в сети. Поскольку это прокси-сервер, он может скрыть от клиента тот факт, что задействована удаленная связь.
Это, в свою очередь, может привести к тому, что клиент будет склонен писать неэффективный код, поскольку он не будет знать, что дорогостоящий сетевой вызов выполняется в фоновом режиме.
Вывод
Шаблон проектирования прокси-сервера-это разумный способ использования некоторых дорогостоящих ресурсов или предоставления определенных прав доступа. Он конструктивно похож на шаблоны адаптера и декоратора, хотя и имеет другое назначение.
Прокси-сервер можно использовать в самых разных обстоятельствах, поскольку в программировании часто требуются ресурсы, особенно при работе с базами данных и сетями.
Поэтому знание того, как эффективно получить доступ к этим ресурсам при обеспечении надлежащего контроля доступа, имеет решающее значение для создания масштабируемых и безопасных приложений.