Чем отличаются шаблоны прокси, Декоратора, адаптера и Моста?

StackOverflow https://stackoverflow.com/questions/350404

Вопрос

Я смотрел на шаблон прокси, и мне он кажется ужасно похожим на шаблоны Декоратора, адаптера и Моста.Я что-то недопонимаю?В чем разница?Почему я должен использовать шаблон прокси по сравнению с другими?Как вы использовали их в прошлом в реальных проектах?

Это было полезно?

Решение

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

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

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

Адаптер

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

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

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

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

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

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

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

Декоратор

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

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

Декоратор должен быть подклассом интерфейса subject.Они могут быть прозрачно использованы вместо его предметов.Смотрите BufferedOutputStream , он по-прежнему является OutputStream и может использоваться как таковой.Это основное техническое отличие от Адаптеров.

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

Прокси - сервер

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

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

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

  • Прокси с отложенной загрузкой – полностью инициализирует объект только при первом использовании или при первом интенсивном использовании.

  • Прокси-сервер доступа – контролирует доступ к теме.

Фасад

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

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

Мост

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

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

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

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

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

  • Фасад constructor принимает корневой элемент всего объектного графа, в остальном он выглядит так же, как Adapter .

Пример из реальной жизни – Сортировочный Адаптер JAXB.Назначение этого адаптера - сопоставление простого плоского класса с более сложной структурой, требуемой извне, и предотвращение "загрязнения" предметного класса чрезмерными аннотациями.

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

Мое понимание закономерностей увеличилось в 100 раз после прочтения Шаблоны проектирования в первую очередь.

Я очень рекомендую это!

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

Я так и сделаю украшать ключевые моменты.

Декоратор:

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

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

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

Прокси - сервер:

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

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

Адаптер:

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

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

Мост:

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

например ,Классы коллекции в java.util. List реализованный с помощью ArrayList.

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

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

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

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

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

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

Они очень похожи, и линии между ними довольно серые.Я предлагаю вам прочитать Шаблон прокси - сервера и Шаблон Декоратора записи в c2 wiki.

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

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

Это цитата из Шаблоны проектирования в первую очередь

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

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

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

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

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

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

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

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

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

Я использую его довольно часто при использовании веб-сервисов.Шаблон прокси, вероятно, следует переименовать во что-то более прагматичное, например, в "Шаблон-оболочку".У меня также есть библиотека, которая является прокси-сервером для MS Excel.Это позволяет очень легко автоматизировать Excel, не беспокоясь о справочных деталях, таких как установленная версия (если таковая имеется).

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

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

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

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

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

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

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

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 { ... }
  • Декоратор также называется "Интеллектуальный прокси". Это используется, когда вы хотите добавить функциональность объекту, но не путем расширения этого объекта тип.Это позволяет вам делать это во время выполнения.

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 { ... }
  • Адаптер используется, когда у вас есть абстрактный интерфейс, и вы хотите сопоставить этот интерфейс с другим объектом, который имеет аналогичную функциональную роль, но другой интерфейс.

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

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

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

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

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

Я верю, что код даст четкие идеи (в дополнение к ответам других пользователей).Пожалуйста, смотрите Ниже (Сосредоточьтесь на типах, которые класс реализует и переносит)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            /* Proxy */

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("PROXY");
            Console.WriteLine(Environment.NewLine);

            //instead of creating here create using a factory method, the facory method will return the proxy
            IReal realProxy = new RealProxy();
            Console.WriteLine("calling do work with the proxy object ");
            realProxy.DoWork();

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("ADAPTER");
            Console.WriteLine(Environment.NewLine);

            /*Adapter*/
            IInHand objectIHave = new InHand();
            Api myApi = new Api();
            //myApi.SomeApi(objectIHave); /*I cant do this, use a adapter then */
            IActual myAdaptedObject = new ActualAdapterForInHand(objectIHave);
            Console.WriteLine("calling api with  my adapted obj");
            myApi.SomeApi(myAdaptedObject);


            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("DECORATOR");
            Console.WriteLine(Environment.NewLine);

            /*Decorator*/
            IReady maleReady = new Male();
            Console.WriteLine("now male is going to get ready himself");
            maleReady.GetReady();

            Console.WriteLine(Environment.NewLine);

            IReady femaleReady = new Female();
            Console.WriteLine("now female is going to get ready her self");
            femaleReady.GetReady();

            Console.WriteLine(Environment.NewLine);

            IReady maleReadyByBeautician = new Beautician(maleReady);
            Console.WriteLine("now male is going to get ready by beautician");
            maleReadyByBeautician.GetReady();

            Console.WriteLine(Environment.NewLine);

            IReady femaleReadyByBeautician = new Beautician(femaleReady);
            Console.WriteLine("now female is going to get ready by beautician");
            femaleReadyByBeautician.GetReady();

            Console.WriteLine(Environment.NewLine);

            Console.ReadLine();


        }
    }

    /*Proxy*/

    public interface IReal
    {
        void DoWork();
    }

    public class Real : IReal
    {
        public void DoWork()
        {
            Console.WriteLine("real is doing work ");
        }
    }


    public class RealProxy : IReal
    {
        IReal real = new Real();

        public void DoWork()
        {
            real.DoWork();
        }
    }

    /*Adapter*/

    public interface IActual
    {
        void DoWork();
    }

    public class Api
    {
        public void SomeApi(IActual actual)
        {
            actual.DoWork();
        }
    }

    public interface IInHand
    {
        void DoWorkDifferently();
    }

    public class InHand : IInHand
    {
        public void DoWorkDifferently()
        {
            Console.WriteLine("doing work slightly different ");
        }
    }

    public class ActualAdapterForInHand : IActual
    {
        IInHand hand = null;

        public ActualAdapterForInHand()
        {
            hand = new InHand();
        }

        public ActualAdapterForInHand(IInHand hnd)
        {
            hand = hnd;
        }

        public void DoWork()
        {
            hand.DoWorkDifferently();
        }
    }

    /*Decorator*/

    public interface IReady
    {
        void GetReady();
    }

    public class Male : IReady
    {
        public void GetReady()
        {
            Console.WriteLine("Taking bath.. ");
            Console.WriteLine("Dress up....");
        }
    }

    public class Female : IReady
    {
        public void GetReady()
        {
            Console.WriteLine("Taking bath.. ");
            Console.WriteLine("Dress up....");
            Console.WriteLine("Make up....");
        }
    }

    //this is a decorator
    public class Beautician : IReady
    {
        IReady ready = null;

        public Beautician(IReady rdy)
        {
            ready = rdy;
        }

        public void GetReady()
        {
            ready.GetReady();
            Console.WriteLine("Style hair ");

            if (ready is Female)
            {
                for (int i = 1; i <= 10; i++)
                {
                    Console.WriteLine("doing ready process " + i);
                }

            }
        }
    }

}

Шаблон проектирования - это не математика, это сочетание искусства и разработки программного обеспечения.Нет ничего подобного для этого требования, вы должны использовать прокси, мост и т.д.Шаблоны проектирования создаются для решения проблем.Если вы предвидите проблему с дизайном, то используйте ее.Основываясь на опыте, вы поймете, какой шаблон использовать для конкретной проблемы.Если бы вы хорошо разбирались в принципах solid design, вы бы внедрили шаблон проектирования, не зная, что это шаблон.Распространенным примером являются шаблоны statergy и factory

Следовательно, больше концентрируйтесь на принципах надежного дизайна, принципах чистого кодирования и ttd

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top