Там, где я работаю, мы используем интерфейсы, чтобы мы могли использовать ZCA или Zope Component Architecture , которая представляет собой целую среду для создания компонентов, которые можно заменять и подключать с использованием Interface
s. Мы используем ZCA, так что мы можем справиться со всеми способами индивидуальной настройки для каждого клиента без необходимости разветвления нашего программного обеспечения или для того, чтобы все биты для каждого клиента испортили главное дерево. К сожалению, вики Zope часто бывают довольно неполными. На странице pypi .
есть краткое объяснение большинства функций ZCA.
Я не использую Interface
s для чего-либо вроде проверки, что класс реализует все методы для данного Interface
. Теоретически, это может быть полезно, когда вы добавляете в интерфейс другой метод, чтобы убедиться, что вы запомнили добавление нового метода ко всем классам, которые реализуют интерфейс. Лично я настоятельно предпочитаю создавать новый Interface
вместо модификации старого. Модификация старого Interfaces
обычно является очень плохой идеей, если они находятся в яйцах, выпущенных для pypi или для остальной части вашей организации.
Краткое примечание по терминологии: классы реализуют Interface
s и объекты (экземпляры классов) предоставляют Interface
s. Если вы хотите проверить Interface
, вы должны написать ISomething.implementedBy(SomeClass)
или ISomething.providedBy(some_object)
.
Итак, приведем примеры, где ZCA полезен. Давайте представим, что мы пишем блог, используя ZCA, чтобы сделать его модульным. У нас будет объект BlogPost
для каждого поста, который обеспечит интерфейс IBlogPost
, определенный в нашем удобном яйце my.blog
. Мы также будем хранить конфигурацию блога в BlogConfiguration
объектах, которые предоставляют IBlogConfiguration
. Используя это в качестве отправной точки, мы можем реализовать новые функции, не обязательно касаясь my.blog
.
Ниже приведен список примеров того, что мы можем сделать, используя ZCA, без необходимости изменять базовое яйцо my.blog
. Я или мои коллеги сделали все эти вещи (и сочли их полезными) в реальных проектах для клиентов, хотя в то время мы не создавали блоги. :) Некоторые из вариантов использования здесь могут быть лучше решены с помощью других средств, таких как файл CSS для печати.
Добавление дополнительных представлений (BrowserView
s, обычно регистрируемых в ZCML с директивой browser:page
) ко всем объектам, которые предоставляют IBlogPost
. Я мог бы сделать my.blog.printable
яйцо. Это яйцо будет регистрировать BrowserView с именем print
для IBlogPost
, который отображает сообщение в блоге с помощью Zope Page Template , предназначенного для создания HTML, который хорошо печатается. Затем BrowserView
появится по адресу /path/to/blogpost/@@print
.
Механизм подписки на события в Zope. Скажем, я хочу публиковать RSS-каналы и создавать их заранее, а не по запросу. Я мог бы создать my.blog.rss
яйцо. В этом яйце я бы зарегистрировал подписчика для событий, которые предоставляют IObjectModified (zope.lifecycleevent.interfaces.IObjectModified
), для объектов, которые предоставляют IBlogPost
. Этого подписчика будут вызывать каждый раз, когда атрибут меняется на что-либо, предоставляющее IBlogPost
, и я могу использовать его для обновления всех RSS-каналов, в которых должно появиться сообщение в блоге.
В этом случае может быть лучше иметь событие IBlogPostModified
, которое отправляется в конце каждого из BrowserView
, которые изменяют сообщения в блоге, поскольку IObjectModified
отправляется один раз при каждом изменении атрибута - что может быть слишком часто для производительности.
Адаптеры. Адаптеры эффективно «забрасывают» из одного интерфейса в другой. Для фанатов языка программирования: адаптеры Zope реализуют «открытую» множественную диспетчеризацию в Python (под «открытым» я подразумеваю «вы можете добавить больше случаев из любого яйца»), причем более специфические совпадения интерфейса имеют приоритет над менее специфичными совпадениями (Interface
классы могут быть подклассами друг друга, и это делает именно то, на что вы надеетесь.)
Адаптеры из одного Interface
могут вызываться с очень хорошим синтаксисом, ISomething(object_to_adapt)
, или их можно искать с помощью функции zope.component.getAdapter
. Адаптеры от нескольких Interface
нужно искать с помощью функции zope.component.getMultiAdapter
, которая немного менее привлекательна.
Вы можете иметь более одного адаптера для данного набора Interface
с, различаясь строкой name
, которую вы предоставляете при регистрации адаптера. Имя по умолчанию ""
. Например, BrowserView
на самом деле являются адаптерами, которые адаптируются от интерфейса, на котором они зарегистрированы, и интерфейса, который реализует класс HTTPRequest. Вы также можете найти всех адаптеров, которые зарегистрированы из одной последовательности Interface
s в другую Interface
, используя zope.component.getAdapters( (IAdaptFrom,), IAdaptTo )
, который возвращает последовательность пар (имя, адаптер). Это может быть использовано как очень хороший способ предоставления хуков для подключаемых модулей.
Скажем, я хотел сохранить все посты и настройки моего блога в виде одного большого файла XML. Я создаю яйцо my.blog.xmldump
, которое определяет IXMLSegment
и регистрирует адаптер от IBlogPost
до IXMLSegment
и адаптер от IBlogConfiguration
до IXMLSegment
. Теперь я могу вызвать любой подходящий адаптер для какого-либо объекта, который я хочу сериализовать, написав IXMLSegment(object_to_serialize)
.
Я мог бы даже добавить больше адаптеров от других вещей к IXMLSegment
из яиц, отличных от my.blog.xmldump
. ZCML имеет функцию, позволяющую запускать определенную директиву тогда и только тогда, когда установлено какое-либо яйцо. Я мог бы использовать это, чтобы my.blog.rss
регистрировал адаптер от IRSSFeed
до IXMLSegment
, если my.blog.xmldump
установлен, без зависимости my.blog.rss
от my.blog.xmldump
.
Viewlet
s похожи на маленькие BrowserView
s, которые можно «подписать» на определенное место на странице. Сейчас я не могу вспомнить все детали, но они очень хороши для таких вещей, как плагины, которые вы хотите разместить на боковой панели.
Я не могу вспомнить, являются ли они частью базовой Zope или Plone. Я бы рекомендовал не использовать Plone, если проблема, которую вы пытаетесь решить, на самом деле не нуждается в реальной CMS, поскольку это большой и сложный программный продукт, и он, как правило, довольно медленный.
В любом случае вам не обязательно нужны Viewlet
s, поскольку BrowserView
s могут вызывать друг друга либо с помощью 'object / @@ some_browser_view' в выражении TAL, либо с помощью queryMultiAdapter( (ISomething, IHttpRequest), name='some_browser_view' )
, но они довольно мило независимо.
Маркер Interface
с. Маркер Interface
- это Interface
, который не предоставляет методов и атрибутов. Вы можете добавить маркер Interface
любой объект во время выполнения, используя ISomething.alsoProvidedBy
. Это позволяет вам, например, изменять, какие адаптеры будут использоваться на конкретном объекте и какие BrowserView
s будут определены для него.
Я прошу прощения за то, что я не вдавался в детали, чтобы сразу реализовать каждый из этих примеров, но каждый занял примерно пост в блоге.