Чем отличаются шаблоны Proxy, Decorator, Adapter и Bridge? - PullRequest
369 голосов
/ 08 декабря 2008

Я смотрел на шаблон Proxy, и мне он кажется очень похожим на шаблоны Decorator, Adapter и Bridge. Я что-то неправильно понимаю? Какая разница? Зачем мне использовать шаблон Proxy по сравнению с другими? Как вы использовали их в прошлом в реальных проектах?

Ответы [ 13 ]

605 голосов
/ 08 декабря 2008

Прокси, Декоратор, Адаптер и Мост - все это варианты «обёртывания» класса. Но их использование отличается.

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

  • Декоратор также называется «Smart Proxy». Это используется, когда вы хотите добавить функциональность к объекту, но не расширять тип этого объекта. Это позволяет вам делать это во время выполнения.

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

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

  • Фасад - это высокоуровневый (читай: более простой) интерфейс для подсистемы одного или нескольких классов. Предположим, у вас есть сложная концепция, которая требует представления нескольких объектов. Внесение изменений в этот набор объектов сбивает с толку, потому что вы не всегда знаете, какой объект имеет метод, который вам нужно вызвать. Настало время написать Facade, который предоставляет высокоуровневые методы для всех сложных операций, которые вы можете выполнять с коллекцией объектов. Пример: модель предметной области для школьного раздела, с такими методами, как countStudents(), reportAttendance(), assignSubstituteTeacher() и т. Д.

178 голосов
/ 27 августа 2011

Как говорится в ответе Билла, варианты их использования различны .

Как и их структуры.

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

  • Адаптер и Фасад оба имеют интерфейс, отличный от того, что они обертывают. Но адаптер является производным от существующего интерфейса, а фасад создает новый интерфейс.

  • Мост и Адаптер оба указывают на существующий тип. Но мост будет указывать на абстрактный тип, а адаптер может указывать на конкретный тип. Мост позволит вам выполнить сопряжение реализации во время выполнения, тогда как адаптер обычно не будет.

48 голосов
/ 23 апреля 2013

Мой взгляд на предмет.

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

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

Адаптер

Адаптер адаптирует субъект (адаптай) к другому интерфейсу. Таким образом, мы можем добавить объект в коллекцию номинально разных типов.

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

Адаптеры защищают одну команду от изменчивого кода других команд; инструмент спасения жизни при работе с оффшорными командами; -)

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

Адаптер помогает обойти ограничение Java только для одного наследования. Он может объединять несколько адаптеров в одном конверте, создавая впечатление множественного наследования.

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

В JDK или базовых библиотеках не так много хороших примеров адаптеров. Разработчики приложений создают адаптеры, чтобы адаптировать библиотеки к интерфейсам приложений.

декоратор

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

Декораторы обычно добавляют (прозрачно) функциональность к обернутому объекту, такую ​​как регистрация, шифрование, форматирование или сжатие объекта. Эта новая функциональность может принести много нового кода. Следовательно, декораторы обычно намного «толще», чем адаптеры.

Декоратор должен быть подклассом интерфейса субъекта. Их можно использовать прозрачно вместо своих предметов. Посмотрите BufferedOutputStream, это все еще OutputStream и может использоваться как таковой. Это основное техническое отличие от адаптеров.

Примеры учебников для всего семейства декораторов легко доступны в JDK - Java IO. Все классы, такие как BufferedOutputStream , FilterOutputStream и ObjectOutputStream являются декораторами OutputStream . Они могут быть наслоены луком, где один декоратор украшен снова, добавляя больше функциональности.

Proxy

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

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

  • Удаленный прокси - субъект находится на удаленном сервере, другой JVM или даже не Система Java. Прокси переводит вызовы методов в вызовы RMI / REST / SOAP или все, что необходимо, защищая клиента от подверженности технология.

  • Lazy Load Proxy - полностью инициализировать объект только при первом использовании или первая интенсивностьIve использования.

  • Access Proxy - контроль доступа к теме.

Фасад

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

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

мост

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

Различия в конструкторах

Различия в шаблонах также очевидны при взгляде на их конструкторы.

  • Прокси не переносит существующий объект. В конструкторе нет темы.

  • Декоратор и Адаптер выполняет обертывание уже существующего объекта, и обычно это
    предоставляется в конструкторе.

  • Конструктор фасада принимает корневой элемент целого графа объектов, в противном случае он выглядит так же, как адаптер.

Пример из реальной жизни - JAXB Marshalling Adapter . Назначение этого адаптера - сопоставление простого плоского класса с более сложной структурой, необходимой извне, и для предотвращения «загрязнения» предметного класса чрезмерными аннотациями.

28 голосов
/ 21 декабря 2008

Существует множество совпадений во многих шаблонах GoF. Все они построены на силе полиморфизма и иногда действительно различаются только намерениями. (стратегия против государства)

Мое понимание шаблонов увеличилось в 100 раз после прочтения Шаблоны Head First Design *

Очень рекомендую!

8 голосов
/ 29 января 2016

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

Я буду украшать ключевых точек.

декоратор:

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

например. (с цепочкой): java.io классы пакетов, относящиеся к InputStream & OutputStream интерфейсам

FileOutputStream fos1 = new FileOutputStream("data1.txt");  
ObjectOutputStream out1 = new ObjectOutputStream(fos1);

Proxy:

  1. Используйте его для отложенной инициализации, повышения производительности за счет кэширования объекта и управления доступом к клиенту / вызывающей стороне . Это может обеспечить альтернативное поведение или вызвать реальный объект. Во время этого процесса он может создать новый объект.
  2. В отличие от Decorator , который позволяет связывать объекты, Proxy не позволяет связывать.

например: java.rmi классы пакетов.

адаптер:

  1. Позволяет двум несвязанным интерфейсам работать вместе через разные объекты , возможно, играя одинаковую роль.
  2. Изменяет оригинальный интерфейс .

например. java.io.InputStreamReader (InputStream возвращает Reader)

мост:

  1. Это позволяет абстракциям и реализациям изменяться независимо .
  2. Используется композиция вместо наследования .

например. Коллекционные занятия по java.util. List реализовано ArrayList.

Ключевые примечания:

  1. Адаптер предоставляет другой интерфейс для своей темы. Proxy обеспечивает тот же интерфейс. Декоратор обеспечивает расширенный интерфейс.
  2. Адаптер изменяет интерфейс объекта, Декоратор повышает ответственность объекта.
  3. Декоратор и Прокси имеют разные цели, но схожие структуры
  4. Адаптер заставляет вещи работать после того, как они спроектированы; Мост заставляет их работать раньше, чем они есть.
  5. Мост разработан заранее, чтобы позволить абстракции и реализации варьироваться независимо. Адаптер переоборудован для совместной работы несвязанных классов
  6. Декоратор разработан, чтобы позволить вам добавлять обязанности к объектам без подклассов.

Посмотрите на великолепные вопросы / статьи по SE, касающиеся примеров различных шаблонов проектирования

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

Когда вы используете шаблон моста? Чем он отличается от шаблона адаптера?

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

7 голосов
/ 08 декабря 2008

Они очень похожи, а линии между ними довольно серые. Я предлагаю вам прочитать записи Proxy Pattern и Pattern Decorator в вики c2.

Записи и обсуждения там довольно обширны, и они также ссылаются на другие соответствующие статьи. Кстати, c2 wiki отлично подходит, когда задается вопросом о нюансах между различными шаблонами.

Чтобы суммировать записи c2, я бы сказал, что декоратор добавляет / изменяет поведение, но прокси имеет больше общего с контролем доступа (ленивая реализация, удаленный доступ, безопасность и т. Д.). Но, как я уже сказал, линии между ними серые, и я вижу ссылки на прокси, которые можно легко рассматривать как декораторы, и наоборот.

3 голосов
/ 13 октября 2014

Это цитата из Head First Design Patterns

Определения принадлежит книге. Примеры принадлежат мне.

Декоратор - Не изменяет интерфейс, но добавляет ответственность. Предположим, у вас есть автомобильный интерфейс, когда вы реализуете это для другой модели автомобиля (s, sv, sl), вам может понадобиться добавить больше ответственности для некоторых моделей. Как есть люк на крыше, подушка безопасности и т.д ..

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

Фасад - Упрощает интерфейс. Предположим, у вас есть интерфейс автомобиля, самолета, корабля. На самом деле все, что вам нужно, это класс, который отправляет людей из одного места в другое. Вы хотите, чтобы фасад решал, какой автомобиль использовать. Затем вы собираете все эти ссылки на интерфейс под 1 зонтиком и позволяете ему решать / делегировать, чтобы он был простым.

Head First: «Фасад не только упрощает интерфейс, он отделяет клиента от подсистемы компонентов. Фасады и адаптеры могут охватывать несколько классов, но цель фасада состоит в том, чтобы упростить адаптер предназначен для преобразования интерфейса во что-то другое. "

2 голосов
/ 21 апреля 2013

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

  • Proxy инкапсулирует доступ снаружи внутрь.
  • Декоратор изменяет или расширяет поведение внутреннего с внешним.
  • Адаптер преобразует интерфейс из внутреннего в внешний.
  • Мост отделяет неизменную часть поведения (внешнюю) от переменной или зависимой от платформы части (внутреннюю).

В зависимости от интерфейса между внутренними и внешними объектами:

  • в Прокси интерфейсы одинаковы.
  • in Decorator интерфейсы одинаковы.
  • in Adapter * Интерфейсы формально отличаются, но выполняют одну и ту же цель.
  • in Мост интерфейсы концептуально различны.
1 голос
/ 22 октября 2015

Я хотел бы добавить примеры к ответу Билла Карвинга (что здорово). Я добавляю также некоторые ключевые отличия реализации, которые, как мне кажется, отсутствуют

Цитируемые части взяты из ответа [https://stackoverflow.com/a/350471/1984346] (Билл Карвинг)

Прокси, Декоратор, Адаптер и Мост - все это варианты «обёртывания» класса. Но их использование отличается.

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

ProxyClass и ObjectClass, которые проксируются, должны реализовывать один и тот же интерфейс, поэтому они взаимозаменяемы

Пример - дорогой объект прокси

class ProxyHumanGenome implements GenomeInterface  {
    private $humanGenome = NULL; 

    // humanGenome class is not instantiated at construct time
    function __construct() {
    }

    function getGenomeCount() {
        if (NULL == $this->humanGenome) {
            $this->instantiateGenomeClass(); 
        }
        return $this->humanGenome->getGenomeCount();
    }
} 
class HumanGenome implement GenomeInterface { ... }
  • Декоратор также называется «Smart Proxy». Это используется, когда вы хотите добавить функциональность к объекту, но не путем расширения этого объекта тип. Это позволяет вам делать это во время выполнения.

DecoratorClass должен (мог) реализовывать расширенный интерфейс ObjectClass. Таким образом, ObjectClass можно заменить на DecoratorClass, но не наоборот.

Пример - добавление дополнительных функций

class DecoratorHumanGenome implements CheckGenomeInterface  {

    // ... same code as previous example

    // added functionality
    public function isComplete() {
        $this->humanGenome->getCount >= 21000
    }
}

interface CheckGenomeInterface extends GenomeInterface {

    public function isComplete();

}

class HumanGenome implement GenomeInterface { ... }
  • Адаптер используется, когда у вас есть абстрактный интерфейс, и вы хотите сопоставить этот интерфейс с другим объектом, который имеет аналогичный функционал роль, но другой интерфейс.

Различия в имплантации Прокси, Декоратор, Адаптер

Адаптер предоставляет другой интерфейс для своей темы. Прокси предоставляет тот же интерфейс. Декоратор предоставляет расширенный интерфейс.

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

  • Фасад - это интерфейс более высокого уровня (читай: более простой) для подсистемы один или несколько классов. Предположим, у вас есть сложная концепция, которая требует несколько объектов для представления. Внесение изменений в этот набор объектов сбивает с толку, потому что вы не всегда знаете, какой объект имеет метод, который вам нужно вызвать. Это время, чтобы написать Фасад, который предоставляет высокоуровневые методы для всех сложных операций, которые вы можете сделать в коллекцию предметов. Пример: модель предметной области для школы раздел, с такими методами, как countStudents(), reportAttendance(), assignSubstituteTeacher() и т. Д.

Большая часть информации в этом ответе взята из https://sourcemaking.com/design_patterns,, которую я рекомендую как отличный ресурс для шаблонов проектирования.

1 голос
/ 03 марта 2012

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

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

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

...