Я дивився на шаблон проксі, і мені це здається жахливим, як шаблони декораторів, адаптерів та мостів. Я щось нерозумію? Яка різниця? Чому я використовую шаблон проксі для інших? Як ви використовували їх у минулому в реальних проектах?
Я дивився на шаблон проксі, і мені це здається жахливим, як шаблони декораторів, адаптерів та мостів. Я щось нерозумію? Яка різниця? Чому я використовую шаблон проксі для інших? Як ви використовували їх у минулому в реальних проектах?
Відповіді:
Проксі, декоратор, адаптер та міст - це всі варіанти "обгортання" класу. Але їх використання різне.
Проксі-сервер можна використовувати, коли ви хочете ліниво встановити об'єкт або приховати той факт, що ви викликаєте віддалену службу або контролюєте доступ до об'єкта.
Декоратор також називається "Smart Proxy". Це використовується, коли ви хочете додати функціональність об'єкту, але не шляхом розширення його типу. Це дозволяє зробити це під час виконання.
Адаптер використовується, коли у вас є абстрактний інтерфейс, і ви хочете зіставити цей інтерфейс на інший об'єкт, який має аналогічну функціональну роль, але інший інтерфейс.
Bridge дуже схожий на Adapter, але ми називаємо його Bridge, коли ви визначаєте як абстрактний інтерфейс, так і базову реалізацію. Тобто ви не пристосовуєтесь до якогось застарілого чи стороннього коду, ви є дизайнером всього коду, але вам потрібно мати можливість замінювати різні реалізації.
Фасад - це інтерфейс вищого рівня (читай: простіший) для підсистеми одного або декількох класів. Припустимо, у вас є складна концепція, для якої потрібно представити кілька об'єктів. Внесення змін до цього набору об’єктів заплутано, оскільки ви не завжди знаєте, який об’єкт має метод, який потрібно викликати. Настав час написати Фасад, який надає методи високого рівня для всіх складних операцій, які Ви можете зробити для колекції об’єктів. Приклад: Модель предметної області для шкільної секції, з допомогою методів , таких як countStudents()
, reportAttendance()
, assignSubstituteTeacher()
, і так далі.
Як йдеться у відповіді Білла, випадки їх використання різні .
Так само їх структури.
У обох проксі та декораторів є той самий інтерфейс, що і їх загорнуті типи, але проксі створює екземпляр під кришкою, тоді як декоратор приймає екземпляр у конструкторі.
Адаптер і фасад мають інший інтерфейс, ніж те, що вони обгортають. Але адаптер походить від існуючого інтерфейсу, тоді як фасад створює новий інтерфейс.
Міст і адаптер вказують на існуючий тип. Але міст буде вказувати на абстрактний тип, і адаптер може вказувати на конкретний тип. Міст дозволить вам з’єднати реалізацію під час виконання, тоді як адаптер зазвичай цього не робить.
Моє взяти на цю тему.
Усі чотири візерунки мають багато спільного, усі чотири іноді неофіційно називають обгортками або візерунками для обгортки. Усі використовують композицію, обгортаючи тему і делегуючи виконання в тему в якийсь момент, роблять відображення виклику одного методу в інший. Вони шкодують клієнта від необхідності побудови іншого об'єкта та копіювання всіх релевантних даних. Якщо їх використовувати розумно, вони економить пам'ять та процесор.
Просуваючи слабке з'єднання, вони роблять колись стійкий код менш підданим неминучим змінам і краще читаються для інших розробників.
Перехідник
Адаптер адаптує предмет (адаптер) до іншого інтерфейсу. Таким чином ми можемо додати об'єкт, розміщений до колекції номінально різних типів.
Адаптер виставляє клієнтам лише відповідні методи, може обмежувати всі інші, виявляючи наміри використання для конкретних контекстів, як-от адаптація зовнішньої бібліотеки, робить її менш загальною та більш зосередженою на потребах наших додатків. Адаптери підвищують читабельність та самоопис нашого коду.
Адаптери захищають одну команду від непостійного коду від інших команд; інструмент порятунку життя при роботі з офшорними командами ;-)
Менш зазначеної мети він запобігає надмірному розміщенню приміток. При такій кількості фреймворків на основі анотацій це стає більш важливим, ніж будь-коли.
Адаптер допомагає подолати обмеження Java лише одного успадкування. Він може поєднувати декілька адаптованих під одним конвертом, створюючи враження багаторазового успадкування.
Код мудрий, адаптер "тонкий". Він не повинен додавати багато коду до адаптованого класу, окрім того, щоб просто викликати метод адаптованих та періодичні перетворення даних, необхідні для здійснення таких дзвінків.
У JDK або базових бібліотеках не так багато хороших прикладів адаптера. Розробники додатків створюють Адаптери, щоб адаптувати бібліотеки до інтерфейсів додатків.
Декоратор
Декоратор не тільки делегує, не тільки відображає один метод на інший, вони роблять більше, вони змінюють поведінку деяких предметних методів, він може вирішити взагалі не викликати предметний метод, делегувати інший об’єкт, об’єкт-помічник.
Декоратори, як правило, додають (прозоро) функціональність до загорнутого об'єкта, наприклад, реєстрація, шифрування, форматування або стиснення предмета. Цей новий функціонал може принести багато нового коду. Отже, декоратори, як правило, набагато «товстіші», ніж перехідники.
Декоратор повинен бути підкласом інтерфейсу теми. Їх можна використовувати прозоро замість предметів. Дивіться BufferedOutputStream, він все ще є OutputStream і може бути використаний як такий. Це основна технічна відмінність від адаптерів.
Приклади текстових книг для цілої родини декораторів легко розміщені в JDK - Java IO. Усі класи, такі як BufferedOutputStream , FilterOutputStream та ObjectOutputStream, є декораторами OutputStream . Вони можуть бути шарувати цибулею, де один декоратор знову прикрашається, додаючи більше функціональності.
Проксі
Проксі - не типова обгортка. Об'єднаний об’єкт, тема проксі, може ще не існувати під час створення проксі. Проксі часто створює його всередині. Це може бути важкий об’єкт, створений на вимогу, або це віддалений об'єкт у різних JVM або різних мережевих вузлах і навіть не-Java-об'єкт, компонент у рідному коді. Це зовсім не обов'язково обгортати або делегувати інший об’єкт.
Найбільш типові приклади - віддалені проксі, ініціалізатори важких об'єктів та проксі-сервери доступу.
Віддалений проксі - предмет знаходиться на віддаленому сервері, іншому JVM або навіть не в системі Java. Проксі переводить виклики методу на дзвінки RMI / REST / SOAP або все необхідне, захищаючи клієнта від впливу базової технології.
Lazy Load Proxy - повністю ініціалізувати об'єкт лише першого або першого інтенсивного використання.
Access Proxy - контроль доступу до предмета.
Фасад
Фасад тісно пов'язаний з принципом дизайну Принципом найменших знань (Закон Деметера). Facade дуже схожий на Adapter. Вони обоє загортають, вони обидва відображають один об'єкт на інший, але вони відрізняються за наміром. Фасад вирівнює складну структуру предмета, складний графік об'єкта, спрощуючи доступ до складної структури.
Фасад обгортає складну структуру, надаючи їй плоский інтерфейс. Це заважає клієнтському об'єкту не піддаватися внутрішнім відносинам у предметній структурі, що сприяє вільній зв'язці.
Міст
Більш складний варіант схеми адаптера, де змінюється не тільки реалізація, але й абстракція. Це додає ще одну непряму до делегації. Додаткова делегація - міст. Він відокремлює адаптер навіть від адаптаційного інтерфейсу. Це збільшує складність більше, ніж будь-який інший візерунок упаковки, тому застосовуйте обережно.
Відмінності конструкторів
Відмінності шаблонів також очевидні при погляді на їх конструктори.
Проксі не обертає існуючий об'єкт. У конструкторі немає предмета.
Decorator and Adapter обертає вже наявний об'єкт, і такий, як правило,
надається в конструкторі.
Конструктор фасаду бере корінний елемент цілого графіка об'єкта, інакше він виглядає так само, як Адаптер.
Приклад із реального життя - адаптер маршалінгу JAXB . Призначення цього адаптера - це зіставлення простого плоского класу на більш складну структуру, необхідну зовні, і запобігання "забрудненню" предметного класу надмірними анотаціями.
У багатьох моделях Міністерства фінансів існує велике перекриття. Всі вони побудовані на силі поліморфізму, а іноді лише по-справжньому відрізняються між собою. (стратегія проти держави)
Моє розуміння шаблонів збільшилось у 100 разів, прочитавши шаблони дизайну Head First Design .
Дуже рекомендую!
Усі хороші відповіді експертів уже пояснили, що означає кожен шаблон.
Я прикрашу ключові моменти.
Декоратор:
наприклад (з ланцюжком): java.io
класи пакунків, пов'язані з InputStream
& OutputStream
інтерфейсами
FileOutputStream fos1 = new FileOutputStream("data1.txt");
ObjectOutputStream out1 = new ObjectOutputStream(fos1);
Проксі:
наприклад: java.rmi
пакунки класів.
Адаптер:
наприклад java.io.InputStreamReader
( InputStream
повертає a Reader
)
Міст:
наприклад, колекційні класи в java.util
. List
реалізований ArrayList
.
Основні нотатки:
Перегляньте чудові питання / статті SE щодо прикладів різних моделей дизайну
Коли використовувати візерунок декоратора?
Коли ви використовуєте шаблон моста? Чим вона відрізняється від моделі адаптера?
Вони досить схожі, а лінії між ними досить сірі. Я пропоную вам прочитати записи проксі-візерунка та шаблону декораторів у вікі c2.
Записи та дискусії там досить великі, і вони також посилаються на інші відповідні статті. До речі, вікі c2 є чудовим, коли дивуєшся нюансам між різними візерунками.
Підводячи підсумки записів на c2, я б сказав, що декоратор додає / змінює поведінку, але проксі має більше спільного з контролем доступу (лінивий екземпляр, віддалений доступ, безпека тощо). Але, як я вже сказав, лінії між ними сірі, і я бачу посилання на проксі, які легко можна розглядати як декораторів і навпаки.
Усі чотири візерунки передбачають обгортання внутрішнього предмета / класу із зовнішнім, тому вони дуже схожі за структурою. Я б окреслив різницю за призначенням:
І за варіацією інтерфейсу між внутрішніми та зовнішніми об'єктами:
Це цитата з головних шаблонів дизайну
Визначення належить книзі. Приклади належать мені.
Decorator - не змінює інтерфейс, але додає відповідальності. Припустимо, у вас є автомобільний інтерфейс, коли ви реалізуєте це для іншої моделі автомобіля (s, sv, sl), можливо, вам доведеться додати більше відповідальності за деякі моделі. Як і люк, подушка безпеки тощо.
Адаптер - перетворює один інтерфейс в інший. У вас є автомобільний інтерфейс, і ви хотіли б, щоб він діяв як джип. Тож ви берете машину, модифікуєте її і перетворюєте на джип. Оскільки це не справжній джип. Але діє як джип.
Фасад - спрощує інтерфейс. Припустимо, у вас є інтерфейси автомобіля, літака, судна. Насправді все, що вам потрібно, - це клас, який пересилає людей з одного місця в інше. Ви хочете, щоб фасад вирішив, яким транспортним засобом використовувати. Потім ви збираєте всі ці посилання на інтерфейс під 1 парасолькою і нехай він вирішує / делегує, щоб зробити це просто.
По-перше: "Фасад не тільки спрощує інтерфейс, він від'єднує клієнта від підсистеми компонентів. Фасади та адаптери можуть охоплювати кілька класів, але мета фасаду - спростити, а адаптер - перетворити інтерфейс на щось інше. "
Я використовую його досить часто під час споживання веб-сервісів. Можливо, проксі-шаблон повинен бути перейменований на щось більш прагматичне, наприклад, "Wrapper Pattern". У мене також є бібліотека, яка є проксі для MS Excel. Це дозволяє дуже легко автоматизувати Excel, не турбуючись про деталі фону, наприклад, що встановлена версія (якщо така є).
Якщо говорити про деталізацію, я знаходжу різницю між проксі і декоратором, адаптером, фасадом ... У загальній реалізації цих шаблонів є цільовий об'єкт, обгорнутий об'єктом, що обгороджує. Клієнт використовує об'єкт, що охоплює, замість цільового об'єкта. І цільовий об'єкт насправді відіграє важливу роль всередині деяких методів укладання об'єкта.
Однак у випадку проксі, що додає об'єкт, може відтворити деякі методи самостійно, він просто ініціалізує цільовий об’єкт, коли клієнт викликає деякі методи, необхідні йому цільовому об'єкту, і це лінива ініціалізація. У випадку інших шаблонів об'єкт, що вкладається, практично ґрунтується на цільовому об'єкті. Тому цільовий об'єкт завжди ініціалізується разом із об'єктом, що вкладається в конструктори / сеттери.
Інша справа, проксі-сервер робить саме те, що робить ціль, тоді як інші шаблони додають більше функціональності для націлювання.
Я хотів би додати приклади до відповіді Білла Карвінга (що це чудово. Доречно). Я також додаю деякі ключові відмінності в реалізації, які, на мою думку, відсутні
Цитовані частини - з відповіді [ 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 { ... }
- Декоратор також називається "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 { ... }
- Адаптер використовується, коли у вас є абстрактний інтерфейс, і ви хочете зіставити цей інтерфейс на інший об'єкт, який має аналогічну функціональну роль, але інший інтерфейс.
Відмінності в реалізації Проксі, декоратор, адаптер
Адаптер забезпечує інший інтерфейс для свого предмета. Проксі надає той же інтерфейс. Decorator забезпечує розширений інтерфейс.
Bridge дуже схожий на 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);
}
}
}
}
}
Шаблон дизайну - це не математика, це поєднання мистецтва та програмної інженерії. Для цього не потрібно використовувати проксі, міст та ін. Шаблони дизайну створюються для вирішення проблем. Якщо ви передбачаєте проблему з дизайном, тоді використовуйте її. Виходячи з досвіду, ви дізнаєтесь для конкретної проблеми, який шаблон використовувати. Якщо ви добрі в твердих принципах дизайну, ви б реалізували модель дизайну, не знаючи, що це шаблон. Поширений приклад - стрибки та фабричні зразки
Отже, більше сконцентруйтесь на твердих принципах розробки, принципах чистого кодування та ттд