![Заместитель](/images/patterns/cards/proxy-mini.png?id=25890b11e7dc5af29625ccd0678b63a8)
Заместитель на Ruby
Заместитель — это объект, который выступает прослойкой между клиентом и реальным сервисным объектом. Заместитель получает вызовы от клиента, выполняет свою функцию (контроль доступа, кеширование, изменение запроса и прочее), а затем передаёт вызов сервисному объекту.
Заместитель имеет тот же интерфейс, что и реальный объект, поэтому для клиента нет разницы — работать через заместителя или напрямую.
Сложность:
Популярность:
Применимость: Паттерн Заместитель применяется в Ruby коде тогда, когда надо заменить настоящий объект его суррогатом, причём незаметно для клиентов настоящего объекта. Это позволит выполнить какие-то добавочные поведения до или после основного поведения настоящего объекта.
Признаки применения паттерна: Класс заместителя чаще всего делегирует всю настоящую работу своему реальному объекту. Заместители часто сами следят за жизненным циклом своего реального объекта.
Концептуальный пример
Этот пример показывает структуру паттерна Заместитель, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.rb: Пример структуры паттерна
# Интерфейс Субъекта объявляет общие операции как для Реального Субъекта, так и
# для Заместителя. Пока клиент работает с Реальным Субъектом, используя этот
# интерфейс, вы сможете передать ему заместителя вместо реального субъекта.
#
# @abstract
class Subject
# @abstract
def request
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Реальный Субъект содержит некоторую базовую бизнес-логику. Как правило,
# Реальные Субъекты способны выполнять некоторую полезную работу, которая к тому
# же может быть очень медленной или точной – например, коррекция входных данных.
# Заместитель может решить эти задачи без каких-либо изменений в коде Реального
# Субъекта.
class RealSubject < Subject
def request
puts 'RealSubject: Handling request.'
end
end
# Интерфейс Заместителя идентичен интерфейсу Реального Субъекта.
class Proxy < Subject
# @param [RealSubject] real_subject
def initialize(real_subject)
@real_subject = real_subject
end
# Наиболее распространёнными областями применения паттерна Заместитель
# являются ленивая загрузка, кэширование, контроль доступа, ведение журнала и
# т.д. Заместитель может выполнить одну из этих задач, а затем, в зависимости
# от результата, передать выполнение одноимённому методу в связанном объекте
# класса Реального Субъекта.
def request
return unless check_access
@real_subject.request
log_access
end
# @return [Boolean]
def check_access
puts 'Proxy: Checking access prior to firing a real request.'
true
end
def log_access
print 'Proxy: Logging the time of request.'
end
end
# Клиентский код должен работать со всеми объектами (как с реальными, так и
# заместителями) через интерфейс Субъекта, чтобы поддерживать как реальные
# субъекты, так и заместителей. В реальной жизни, однако, клиенты в основном
# работают с реальными субъектами напрямую. В этом случае, для более простой
# реализации паттерна, можно расширить заместителя из класса реального субъекта.
#
# @param [Subject] subject
def client_code(subject)
# ...
subject.request
# ...
end
puts 'Client: Executing the client code with a real subject:'
real_subject = RealSubject.new
client_code(real_subject)
puts "\n"
puts 'Client: Executing the same client code with a proxy:'
proxy = Proxy.new(real_subject)
client_code(proxy)
output.txt: Результат выполнения
Client: Executing the client code with a real subject:
RealSubject: Handling request.
Client: Executing the same client code with a proxy:
Proxy: Checking access prior to firing a real request.
RealSubject: Handling request.
Proxy: Logging the time of request.