
Заместитель на 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.