Построение строки с использованием шаблона ООП и прокси

Мне очень интересно, как SQLAlchemy создает строки запроса, например:

(Session.query(model.User)
        .filter(model.User.age > 18)
        .order_by(model.User.age)
        .all())

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

Во-первых, какой-то объект, один из множества подобных объектов:

class SomeObject(object):
    items = None

    def __init__(self):
        self.items = []

    def __call__(self):
        return ' '.join(self.items) if self.items is not None else ''

    def a(self):
        self.items.append('a')
        return self

    def b(self):
        self.items.append('b')
        return self

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

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

import operator

class Proxy(object):
    def __init__(self, some_object):
        self.some_object = some_object

    def __getattr__(self, name):
        self.method = operator.methodcaller(name)
        return self

    def __call__(self, *args, **kw):
        self.some_object = self.method(self.some_object, *args, **kw)
        return self

    def perform(self):
        return self.some_object()

И наконец:

>>> obj = SomeObject()
>>> p = Proxy(obj)
>>> print p.a().a().b().perform()
a a b

Что вы можете сказать об этой реализации? Существуют ли более эффективные способы создания желаемого количества классов, которые бы создавали такую ​​строку с одним и тем же синтаксисом?

PS: Извините за мой английский, это не мой основной язык.


person scraplesh    schedule 19.10.2011    source источник
comment
Почему бы вам не посмотреть код SQLAlchemy, чтобы увидеть, как они это делают?   -  person millimoose    schedule 19.10.2011


Ответы (2)


На самом деле то, что вы видите, это не шаблон прокси, а шаблон компоновщика, и да, ваша реализация ИМХО - классический (с использованием шаблона Fluent interface).

person mouad    schedule 19.10.2011
comment
Строго говоря, это не строительный шаблон. Хотя в наши дни называть его так — распространенная ошибка. Это пример внутреннего DSL или свободного программирования, последний термин является наиболее широко используемым. (Шаблон построителя предназначен для построения графа объектов, где вы хотите отделить алгоритм построения - директор - от конкретных созданных объектов, например, Заголовок, Резюме, Индекс, Глава 1, Глава 2 в документе, который может быть либо PDF-документ, либо HTML-документ и т. д.) - person Angel O'Sphere; 19.10.2011
comment
@AngelO'Sphere: Спасибо за подробное объяснение, я не знал, что есть разница между обоими шаблонами (конструктор и свободное программирование), но у меня есть вопрос, если можно: не является ли свободное программирование (интерфейс) распространенный способ реализации шаблона построителя, поэтому, может быть, в этом случае мы можем сказать, что запрос построен с использованием шаблона построителя, который реализует свободный интерфейс? еще раз спасибо за дополнительные знания :) - person mouad; 19.10.2011
comment
Если я правильно помню, в оригинальной GOF-книге Строители не говорили бегло. Однако я всегда свободно говорю на них. Чтобы ответить на ваш вопрос в другом направлении: не все беглые DSL имеют какое-то отношение к построению. Например. платформа FEST использует гибкий API для автоматических тестов, управляемых пользовательским интерфейсом (автоматические щелчки мыши и т. д.) - person Angel O'Sphere; 20.10.2011

Я не знаю, что делает SQLAlchemy, но я бы реализовал интерфейс, заставив метод Session.query() возвращать объект Query с такими методами, как filter(), order_by(), all() и т. д. Каждый из этих методов просто возвращает новый объект Query с учетом примененных изменений. . Это позволяет использовать цепочку методов, как в вашем первом примере.

Ваш собственный пример кода имеет множество проблем. Один пример

obj = SomeObject()
p = Proxy(obj)
a = p.a
b = p.b
print a().perform()  # prints b
person Sven Marnach    schedule 19.10.2011