Различия между шаблоном прокси и декоратором

Можете ли вы дать хорошее объяснение, в чем разница между Proxy и Decorator?

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

Вопрос в том, является ли Прокси, созданный с помощью агрегации, по-прежнему Прокси или, скорее, Декоратором? Разрешено ли (по определению в шаблонах GoF) создавать прокси с агрегацией?


person Łukasz Rzeszotarski    schedule 04.09.2013    source источник
comment
Некоторые ссылки: Proxy и Декоратор   -  person Sotirios Delimanolis    schedule 04.09.2013
comment
Откуда вы взяли, что Proxy использует композицию, а Decorator - агрегацию?   -  person CPerkins    schedule 04.09.2013
comment
@CPerkins см. Мой комментарий к ответу Рахула Трипати.   -  person Łukasz Rzeszotarski    schedule 04.09.2013
comment
А также декоратор (patterns.cs.up.ac.za/ examples / ch2 / decorator-theory.cs) - очевидно, агрегация, прокси (patterns.cs.up.ac.za/examples/ch2/proxy-theory.cs) - очевидно, композиция.   -  person hyankov    schedule 13.03.2016


Ответы (8)


Вот прямая цитата из GoF (стр. 216).

Хотя декораторы могут иметь такую ​​же реализацию, что и прокси, у декораторов другое предназначение. Декоратор добавляет одну или несколько обязанностей к объекту, тогда как прокси контролирует доступ к объекту.

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

Популярные ответы показывают, что Прокси-сервер знает конкретный тип своего делегата. Из этой цитаты мы видим, что это не всегда так.

Разница между прокси и декоратором в соответствии с GoF заключается в том, что прокси ограничивает клиента. Декоратора нет. Прокси-сервер может ограничивать действия клиента делать, контролируя доступ к функциям; или он может ограничить то, что клиент знает, выполняя действия, которые невидимы и неизвестны клиенту. Decorator делает противоположное: он улучшает то, что делает его делегат, так, чтобы это было видно клиентам.

Можно сказать, что Proxy - это черный ящик, а Decorator - это белый ящик.

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

  • Decorator информирует и уполномочивает своего клиента.
  • Прокси-сервер ограничивает и лишает возможности своего клиента.
person jaco0646    schedule 01.03.2020
comment
В случае, если (например, защитный доступ) прокси реализован точно так же, как декоратор, является ли причина различия между ними исключительно для передачи намерения: ограничить доступ или улучшить поведение? - person Sebastian Nielsen; 06.01.2021
comment
Согласно GoF, да. Они часто подчеркивают намерение при различении паттернов. Я бы добавил, что клиент с меньшей вероятностью будет знать о прокси, чем о декораторе, а это означает, что кто создает его экземпляр, является важным отличием. - person jaco0646; 06.01.2021
comment
А если это смесь? Что, если оболочка одновременно увеличивает и ограничивает своего клиента? Это проблема концентрации на намерении. Это по-прежнему хороший ответ, и похоже, что GoF имел в виду именно его. Но легко неправильно диагностировать намерение или просто не согласиться субъективно. В большинстве случаев достаточно взглянуть на реализацию оболочки. - person cdunn2001; 10.07.2021

Настоящая разница не в праве собственности (состав или агрегирование), а скорее в информации о типе.

Декоратор всегда передается своему делегату. Прокси может создать его сам или может внедрить его.

Но Прокси всегда знает (более) конкретный тип делегата. Другими словами, Прокси и его делегат будут иметь один и тот же базовый тип, но Прокси указывает на некоторый производный тип. Декоратор указывает на собственный базовый тип. Таким образом, разница заключается в информации о типе делегата во время компиляции.

На динамическом языке, если делегат внедрен и имеет тот же интерфейс, тогда нет никакой разницы.

Ответ на ваш вопрос - «Да».

person cdunn2001    schedule 08.08.2014
comment
Но Прокси всегда знает (более) конкретный тип делегата. Не думаю, что это правда. Представьте себе удаленный прокси. Механизму проксирования не нужно знать какие-либо особенности удаленного объекта. Удаленная система регистрирует объект с указанным интерфейсом. И локальный прокси предоставляет тот же интерфейс. - person Alexey; 22.08.2015
comment
Я прошел курс по этому поводу в Amazon от приглашенного лектора, который знал свое дело. Существует разница между использованием исполняемого файла прокси (например, с веб-службой) и шаблона проектирования прокси. UML шаблона Proxy и шаблона Decorator могут быть разными. Но ничто не мешает прокси-серверу иметь тот же API, что и его делегат. Декоратор - это строгое подмножество прокси, но декоратор все равно может называться прокси в зависимости от того, гарантированно ли базовый API будет таким же. - person cdunn2001; 28.08.2015

Шаблон Декоратор предназначен для динамического добавления функций к объекту, а шаблон Прокси предназначен для управления доступом к объекту.

РЕДАКТИРОВАТЬ: -

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

person Rahul Tripathi    schedule 04.09.2013
comment
Тем не менее, прокси можно использовать для добавления функций. Подумайте о прокси AOP. - person Sotirios Delimanolis; 04.09.2013
comment
Полностью согласен, сэр. Другими словами, я бы хотел преобразовать то, что я имел в виду, было с Proxy Pattern, прокси-класс может скрывать подробную информацию об объекте от своего клиента. Поэтому при использовании Proxy Pattern мы обычно создаем экземпляр abject внутри прокси-класса. А при использовании шаблона декоратора мы обычно передаем исходный объект в качестве параметра конструктору декоратора. - person Rahul Tripathi; 04.09.2013
comment
В этом случае, когда экземпляр «скрыт» в прокси-сервере, разница для меня очевидна (как я уже писал), однако я думаю, что часто люди вызывают как прокси-классы, которые принимают прокси-объект, который был передан как параметр конструктора. В этом случае для меня (очень) тонка разница в добавлении новых функций или управлении. - person Łukasz Rzeszotarski; 04.09.2013
comment
Связь между прокси и реальным объектом обычно устанавливается во время компиляции, прокси каким-то образом создает его экземпляр, тогда как декоратор или адаптер назначаются субъекту во время выполнения, зная только интерфейс субъекта. Надеюсь, это имеет смысл !!! :) - person Rahul Tripathi; 04.09.2013
comment
Вы могли бы добавить эту строчку к своему ответу. - person Łukasz Rzeszotarski; 04.09.2013
comment
У меня возник тот же вопрос, надеюсь, обсуждение здесь будет актуальным и полезным ... stackoverflow.com/questions/36266690/ - person nits.kk; 09.11.2016

Декоратор получает ссылку на оформленный объект (обычно через конструктор), а Прокси отвечает за это самостоятельно.

Прокси может вообще не создавать экземпляр объекта упаковки (например, ORM для предотвращения ненужного доступа к БД, если поля / геттеры объекта не используются), в то время как Decorator всегда содержит ссылку на фактическую упаковку. пример.

Прокси-сервер обычно используется фреймворками для добавления безопасности или кэширования / бездействия и создается фреймворком (а не самим обычным разработчиком).

Декоратор обычно используется для добавления нового поведения к старым или устаревшим классам самим разработчиком на основе интерфейса, а не фактического класса (поэтому он работает с широким спектром экземпляров интерфейса, Прокси доступен бетонный класс).

person gavenkoa    schedule 23.10.2013

Ключевые отличия:

  1. Прокси-сервер предоставляет тот же интерфейс. Декоратор предоставляет улучшенный интерфейс.
  2. Декоратор и Прокси имеют разные цели, но похожую структуру. Оба описывают, как обеспечить уровень косвенного обращения к другому объекту, а реализации сохраняют ссылку на объект, которому они направляют запросы.
  3. Декоратор можно рассматривать как вырожденный композит, содержащий только один компонент. Однако декоратор добавляет дополнительные обязанности - он не предназначен для агрегирования объектов.
  4. Декоратор поддерживает рекурсивную композицию
  5. Класс Decorator объявляет связь состав с интерфейсом LCD (наименьший знаменатель класса), и этот член данных инициализируется в его конструкторе.
  6. Используйте Прокси для отложенной инициализации, повышения производительности путем кэширования объекта и контроля доступа к клиенту / вызывающей стороне

В статье Sourcemaking превосходно цитируются сходства и различия.

Связанные вопросы / ссылки по SE:

Когда использовать шаблон декоратора?

В чем точная разница между адаптером и Шаблоны прокси?

person Ravindra babu    schedule 07.04.2016

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

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

person James Lin    schedule 07.02.2018
comment
Что вы говорите, что отличается от других 4 ответов, которые уже здесь? - person Stephen Rauch; 07.02.2018
comment
Не знаю, все ли там есть. Я просто почувствовал побуждение вмешаться после прочтения предыдущих ответов. - person James Lin; 08.02.2018

Потребовалось время, чтобы понять этот ответ и понять, что он на самом деле означает. Несколько примеров должны прояснить ситуацию.

Proxy первый:

public interface Authorization {
    String getToken();
} 

А также :

// goes to the DB and gets a token for example
public class DBAuthorization implements Authorization {
    @Override
    public String getToken() {
        return "DB-Token";
    }
}

И есть вызывающий этого Authorization, довольно тупой:

class Caller {
    void authenticatedUserAction(Authorization authorization) {
        System.out.println("doing some action with : " + authorization.getToken());
    }
}

Пока что ничего необычного, правда? Получите токен от определенной службы, используйте этот токен. Теперь появляется еще одно требование к картинке, добавьте ведение журнала: это означает, что каждый раз записывать токен. В этом случае все просто, просто создайте Proxy:

public class LoggingDBAuthorization implements Authorization {

    private final DBAuthorization dbAuthorization = new DBAuthorization();

    @Override
    public String getToken() {
        String token = dbAuthorization.getToken();
        System.out.println("Got token : " + token);
        return token;
    }
}

Как бы мы это использовали?

public static void main(String[] args) {
    LoggingDBAuthorization loggingDBAuthorization = new LoggingDBAuthorization();

    Caller caller = new Caller();
    caller.authenticatedUserAction(loggingDBAuthorization);
}

Обратите внимание, что LoggingDBAuthorization содержит экземпляр DBAuthorization. И LoggingDBAuthorization, и DBAuthorization реализуют Authorization.

  • Прокси-сервер будет содержать некоторую конкретную реализацию (DBAuthorization) базового интерфейса (Authorization). Другими словами, прокси-сервер точно знает, что проксируется.

Decorator:

Он начинается примерно так же, как Proxy, с интерфейсом:

public interface JobSeeker {
    int interviewScore();
}

и его реализация:

class Newbie implements JobSeeker  {
    @Override
    public int interviewScore() {
        return 10;
    }
}

А теперь мы хотим добавить более опытного кандидата, который добавляет свой балл на собеседовании плюс балл другого JobSeeker:

@RequiredArgsConstructor 
public class TwoYearsInTheIndustry implements JobSeeker {

    private final JobSeeker jobSeeker;

    @Override
    public int interviewScore() {
        return jobSeeker.interviewScore() + 20;
    } 
}

Обратите внимание, как я сказал, что плюс один от другого соискателя работы, не Newbie. Decorator не знает точно, что он украшает, он знает только контракт этого украшенного экземпляра (он знает о JobSeeker). Обратите внимание, что это не похоже на Proxy; который, напротив, точно знает, что украшает.

Вы можете спросить, есть ли на самом деле разница между двумя шаблонами проектирования в этом случае? Что, если мы попытаемся записать Decorator как Proxy?

public class TwoYearsInTheIndustry implements JobSeeker {

    private final Newbie newbie = new Newbie();

    @Override
    public int interviewScore() {
        return newbie.interviewScore() + 20;
    }
}

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

person Eugene    schedule 24.10.2019

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

person Ali Bayat    schedule 14.11.2019