Интерфейс-маркер (шаблон проектирования)

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

Интерфейс-маркер, маркер (англ. marker interface pattern) — это шаблон проектирования, применяемый в языках программирования с проверкой типов во время выполнения. Шаблон предоставляет возможность связать метаданные (интерфейс) с классом даже при отсутствии в языке явной поддержки для метаданных.

Чтобы использовать эту модель, класс реализует интерфейс[1] («помечается интерфейсом»), а взаимодействующие с классом методы проверяют наличие интерфейса. В отличие от обычного интерфейса, который определяет функциональность (в виде объявлений методов и свойств), которой должен обладать реализуемый класс объектов, важен сам факт обладания класса маркером. Маркер лишь является признаком наличия определённого поведения у объектов класса, помеченного маркером. Разумеется, возможны и «смешанные» интерфейсы, однако при неаккуратном использовании они могут создавать путаницу.

Пример применения маркеров-интерфейсов в языке программирования Java является интерфейс Serializable. Класс должен реализовать этот интерфейс, чтобы показать, что его экземпляры могут быть записаны в ObjectOutputStream. Класс ObjectOutputStream имеет публичный метод writeObject(), который содержит ряд instanceof проверок возможности записи, одной из которых является интерфейс Serializable. Если вся серия проверок оканчивается неудачей, метод выбрасывает исключение NotSerializableException.

Другим примером является интерфейс INamingContainer, который определен в .NET Framework. INamingContainer определяет элемент управления контейнером, который создает новый идентификатор пространства имен в иерархии элементов управления объекта Page.[2]. Любой элемент управления, который реализует этот интерфейс, создает новое пространство имен, в котором обеспечивается уникальность всех идентификаторов атрибутов дочерних элементов управления в пределах всего приложения. При разработке шаблонных элементов управления необходимо реализовывать этот интерфейс, чтобы избежать конфликтов именования на странице.

Применение[править | править код]

Класс Repeater является элементом управления и представляет собой список с привязкой к данным, который определен в .NET Framework (ASP.net). Данный элемент позволяет создавать разметку путём повторения указанного шаблона для каждого элемента списка. Во избежание конфликтов имен класс помечается интерфейсом INamingContainer.

public interface INamingContainer { }
public class Control : IComponent, ...
{
    ...
    internal bool IsBindingContainer
    {
        get
        {
            return ((this is INamingContainer) && !(this is INonBindingContainer));
        }
    }
    ...
}
public class Repeater : Control, INamingContainer
{
...
}

Исходя из свойства IsBindingContainer исполняющая среда во время генерации страницы дополняет новым пространством имен идентификаторы элементов управления, находящихся в элементе управления Repeater.

Преимущества[править | править код]

Преимуществом использования маркера является возможность внести в языки, не поддерживающие метаданные, дополнительную информацию об особенностях поведения класса. При этом в некоторых языках, например чтобы извлечь информацию из метаданных требуется больше времени, что при частом использовании отразится на производительности. Так в языке C#, по производительности можно сравнивать конструкцию (для проверки поддержки некоторого поведения или особенностей), когда используется интерфейс-маркер:

control is INamingContainer

и конструкцию с использованием атрибутов и механизма отражения:

control.GetType().IsDefined(typeof(NamingContainerAttrubute), false)

Кроме этого, некоторые языки (например, Java с использованием библиотек ASM  (англ.), Javassist  (англ.) или Similar[источник не указан 1845 дней]; и другие языки[какие?]) позволяют создавать или генерировать классы (и интерфейсы), помечая классы интерфейсом-маркером динамически во времени выполнения. Атрибуты или метаданные обычно связываются с классами уже во время компиляции, что приводит к невозможности изменить их поведение в дальнейшем.

Недостатки[править | править код]

Одной из основных проблем с маркером является то, что интерфейс определяет контракт на реализацию классов, и что контракт наследуется всеми подклассами. Это означает, что вы не можете «отменить лишние реализации» маркером. В приведённом примере, если вы создаёте подкласс, и не хотите его сериализовать (возможно, потому что это зависит от частичной реализации), вы должны явно бросать исключение NotSerializableException (согласно документации ObjectOutputStream).

Решением описанной проблемы является поддержка метаданных непосредственно в синтаксисе языка:

  • Такие языки как .NET framework и Java (начиная с версии Java 5 (1.5)) имеют поддержку таких метаданных. В .NET, они называются «пользовательскими атрибутами», в Java их называют «аннотациями». Несмотря на разные названия, они концептуально равнозначны, могут быть определены для классов, переменных, методов и параметров методов, а также быть доступны с помощью отражения.
  • В Python термин англ. marker interface применяется в компонентной архитектуре Zope (Zope Component Architecture, ZCA) и построенной на его базе системе управления содержимым Plone. В ZCA маркерами могут отмечаться не только классы, но и отдельные объекты.[3]

См. также[править | править код]

Примечания[править | править код]

  1. Bloch, Joshua. Item 37: Use marker interfaces to define types // Effective Java (Second edition) (неопр.). — Addison-Wesley, 2008. — С. 179. — ISBN 978-0-321-35668-0.
  2. INamingContainer - интерфейс (System.Web.UI). Дата обращения: 22 ноября 2013. Архивировано 1 сентября 2013 года.
  3. BlueBream v1.0b4 documentation, Tutorial

Литература[править | править код]

  • Joshua Bloch, "Effective Java (Second edition), " Item 37: Use marker interfaces to define types, page 179.