Использование интерфейсов.txt
2008-01-11 19:02...
Использование интерфейсов:
Рассказывая про компонентную модель мы упустили из виду чисто практический аспект программирования с использованием интерфейсов: как определять интерфейс, откуда его брать, как его декларировать для обычного класса. Попробуем ответить на эти вопросы в рамках данной статьи.
Сразу разделим, как это сделано в определении компонентной модели, два понятия: интерфейс и схема интерфейса. Про схемы будет рассказано в статье Использование схем интерфейсов.txt.
Определение интерфейса:
Интерфейс это класс специального метакласса (или, по простому, специального вида). Он порождается от класса zope.interface.Interface примерно вот так:
from zope.interface import Interface
class IA(Interface) :
""" Это просто тестовый интерфейс A"""
Обычно такие определения располагаются в файле interfaces.py вашего продукта.
Интерфейсы можно порождать друг от друга, и при этом при их использовании будут выполнятся правила наследования, правда, не всегда тривиальным способом (об этом позаботится как достаточно нетривиальная логика использования интерфейсов, так и кривая реализация поддержки этой логики):
class IB(Interface) :
""" Это просто тестовый интерфейс B """
class IAB(IA,IB) :
""" Это тестовый интерфейс от A и B """
В последнем случае компонент, предоставляющий интерфейс IAB, будет найден и при поиске компонента, предоставляющего IA, и при поиске компонента предоставляющего IB. Поскольку все интерфейсы наследуются от интерфейса Interface, во всех определениях Interface - синоним "любой интерфейс".
Интерфейс можно зарегистрировать директивой ZCML:
<zope:interface
interface=".interfaces.IA"
name="qq"
type="zope.app.content.interfaces.IContentType" />
Соль этой директивы состоит в том, что интерфейс регистрируется как утилита, предоставляющая интерфейс type (тип интерфейса) и под именем "qq". Т.е. если потребуется неизвестно откуда достать интерфейс определенного типа и имени, это можно сделать даже не зная код, в котором он определен. Это позволяет избавится от явных зависимостей между продуктами, содержащими компоненты и является интересным и опасным инструментом, который, похоже, практически не находит применения, хотя порывшись по исходникам можно начитатся всякого. Например, если интерфейс имеет тип IContentType, то предоставляющая его компонента будет считаться контент-типом, из чего, видимо, будут делаться соответствующие выводы.
Зарегистрированный интерфейс можно найти, используя обычный реестр утилит
Декларация интерфейса:
Для любого класса можно декларировать интерфейс. Обычно это делается таким образом:
from zope.interface import implements
from interfaces import IA
class A(object) :
implements(IA)
Такой способ удобен, если класс пишете вы сами. Однако бывают исключения - либо класс просто написали другие люди, не думая ни о каких компонентных моделях, а вам их продукт жизненно необходимо завязать в компоненты, либо сама декларация интерфейса навязывается классу в целях его настройки, как описано в неизвестно где. Оба случая часто встречаются, и в этом случае используют либо специальную функцию:
zope.interface.classImplements(A,IA,IB,IC,...)
Либо, что чаще, директиву ZCML:
<zope:class class="a.A">
<implements interface="interfaces.IA">
</zope:class>
Рекомендуется использовать первый случай, если это интерфейс, который необходим для описания неотъемлемого поведения класса, и второй случай, если это интерфейс, необходимый для настройки поведения класса в рамках конкретной установки с другими продуктами, как описано в неизвестно где.
Как узнать о том, что компонент (экземпляр класса) декларирует интерфейс:
Есть минимум три способа это сделать, из которых верен только один: не узнавать это вообще, а вовлечь класс во взаимодействие: пусть компоненнтная среда, реестры, утилиты да адаптеры, подберут что нужно, чтобы сделать то, что надо сделать, или скажут, что это невозможно. Простейший способ сделать это:
IA(a,None)
Про более сложные случаи рассказывалось во введении в компоненты.
Ну и два плохих способа:
IA.providedBy(a)
from zope.interface import providedBy
IA in providedBy(a)
Если в коде встречаются такие конструкции, то это свидетельствует либо о какой-то архитектурной ошибке в приложении, либо об очень нетривиальных решаемых задачах, выходящих за рамки компонентной модели.
Заключение:
На этом использование интерфейса просто как декларации возможности взаимодействия заканчивается. И, на самом деле, этого действительно достаточно для очень многих задач. Однако рассказ об использовании схем интефейсов более обширен.



