Если программа включает в себя достаточно «тяжёлые» объекты, которые потребляют достаточно много ресурсов, наиболее оптимальное решение – создавать такие объекты «по запросу». То есть тогда, когда они на самом деле необходимы.
Для решения этой задачи и предназначен паттерн «Заместитель».
Описание
Паттерн «Заместитель» является своего суррогатом другого объекта и контролирует доступ к нему [1].
По сути, «Заместитель» представляет собой своего рода «заглушку», которая помещается в том месте программного кода, где необходимо вызвать «тяжелый» объект. Так как «Заместитель» потребляет значительно меньше ресурсов, то при начальной инициализации получается их значительная экономия. Когда нужно вызвать непосредственно сам объект, достаточно просто отправить соответствующий запрос «Заместителю».
В качестве примера можно привести получение объекта содержащего массив сведений из базы данных тогда, когда он действительно необходим.
Ниже представлена диаграмма классов паттерна «Заместитель».
Паттерн «Заместитель» состоит из трёх участников.
- RealSubject – Реальный субъект. Определяет реальный объект представленный заместителем;
- Proxy – собственно «заместитель»;
- Subject – субъект. Определяет общий интерфейс для RealSubject и Proxy. Благодаря этому и становится возможным использование Proxy там, где ожидается RealSubject.
Может быть реализован в виде абстрактного базового класса или интерфейса.
Класс Proxy выполняет следующие функции:
- Хранит ссылку, которая позволяет обратиться к реальному субъекту;
- Предоставляет интерфейс аналогичный интерфейсу реального субъекта. Поэтому класс Proxy всегда может быть подставлен вместо реального субъекта;
- Контролирует доступ к реальному субъекту и может отвечать за его создание и удаление.
Пример использования
В качестве примера реализации паттерна «Заместитель» рассмотрим простой калькулятор на Delphi.
Создадим абстрактный класс Calc в котором объявим методы для арифметических операций сложения, вычитания, умножения и деления. Затем класс RealCalc, в котором все эти методы будут реализованы. И, наконец, класс CalcProxy, который будет служить «Заместителем» для класса RealCalc.
Код модуля со всеми тремя классами приведён ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
unit CalculatorPatternProxy; interface type { ***Субъект*** } Calc = class abstract public // Сложение function Addition(a, b: double): double; virtual; abstract; // Деление function Division(a, b: double): double; virtual; abstract; // Умножение function Multiplication(a, b: double): double; virtual; abstract; // Вычитание function Substraction(a, b: double): double; virtual; abstract; end; type { ***Реальный субъект*** } RealCalc = class(Calc) Public // Сложение function Addition(a, b: double): double; override; // Деление function Division(a, b: double): double; override; // Умножение function Multiplication(a, b: double): double; override; // Вычитание function Substraction(a, b: double): double; override; end; type CalcProxy = class(Calc) { ***Заместитель*** } Private Calculator: Calc; public // Сложение function Addition(a, b: double): double; override; // Деление function Division(a, b: double): double; override; // Умножение function Multiplication(a, b: double): double; override; // Вычитание function Substraction(a, b: double): double; override; end; implementation { RealCalc } function RealCalc.Addition(a, b: double): double; begin result := a + b; end; function RealCalc.Division(a, b: double): double; begin result := a / b; end; function RealCalc.Multiplication(a, b: double): double; begin result := a * b; end; function RealCalc.Substraction(a, b: double): double; begin result := a - b; end; { CalcProxy } function CalcProxy.Addition(a, b: double): double; begin if (not Assigned(Calculator)) then Calculator := RealCalc.Create; result := Calculator.Addition(a, b); end; function CalcProxy.Division(a, b: double): double; begin if (not Assigned(Calculator)) then Calculator := RealCalc.Create; result := Calculator.Division(a, b); end; function CalcProxy.Multiplication(a, b: double): double; begin if (not Assigned(Calculator)) then Calculator := RealCalc.Create; result := Calculator.Multiplication(a, b); end; function CalcProxy.Substraction(a, b: double): double; begin if (not Assigned(Calculator)) then Calculator := RealCalc.Create; result := Calculator.Substraction(a, b); end; end |
[stextbox id=»info» caption=»Примечание»]
1 2 3 4 5 6 7 8 9 10 |
Calc = class public // Сложение function Addition(a, b: double): double; virtual; abstract; // Деление function Division(a, b: double): double; virtual; abstract; // Умножение function Multiplication(a, b: double): double; virtual; abstract; // Вычитание function Substraction(a, b: double): double; virtual; abstract; end; |
Для того чтобы подставить заместитель вместо реального субъекта нужно объявить переменную (или поле класса) типа субъект.
1 |
c: Calc; |
1 |
c := CalcProxy.Create; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
procedure TMain.btnAddClick(Sender: TObject); begin TextResult.Caption := FloatToStr(c.Addition(a, b)); end; procedure TMain.btnDivClick(Sender: TObject); begin TextResult.Caption := FloatToStr(c.Division(a, b)); end; procedure TMain.btnMulClick(Sender: TObject); begin TextResult.Caption := FloatToStr(c.Multiplication(a, b)); end; procedure TMain.btnSubClick(Sender: TObject); begin TextResult.Caption := FloatToStr(c.Substraction(a, b)); end; |
Контроль доступа к реальному объекту
«Заместитель» также может реализовывать контроль доступа к замещаемому им объекту (реальному субъекту).
Можно включить в код «Заместителя» алгоритмы, которые в зависимости от определённых условий, либо предоставляют доступ к реальному субъекту либо отклоняют запрос.
Для примера, усовершенствуем метод деления в классе CalcProxy и запретим деление на ноль.
1 2 3 4 5 6 7 8 9 10 11 |
function CalcProxy.Division(a, b: double): double; begin if b 0 then begin if (not Assigned(Calculator)) then Calculator := RealCalc.Create; result := Calculator.Division(a, b); end else raise EZeroDivide.Create('Делить на ноль нельзя!'); end; |
К слову, не смотря на то, что эта функция работает с вещественным типом данных, приведённый код реально работает.
Можно написать, например, следующий код и проверить.
1 2 3 |
a:=15; b:=0; TextResult.Caption := FloatToStr(c.Division(a, b)); |
Таким образом, паттерн «Заместитель» позволяет не только оптимизировать использование ресурсов ЭВМ при начальной инициализации программы, но и управлять доступом к замещаемому объекту инкапсулируя соответствующие алгоритмы.
Источники:
- Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приёмы объектно-ориентированного проектирования. Паттерны проектирования.
Добавить комментарий