Как определять и реализовывать Интерфейсы?

Как определять Интерфейсы?

Большая часть определения интерфейса уже показана “Non-instantiable classed types: Interfaces.” но я чувствую что нужно показать как конкретно создаётся интерфейс. Исходные коды примеров связанных с этим разделом можно найти в исходном пакете документации, в файле sample/interface/maman-ibaz.{h|c}.

Как и раньше, сначала получаем правильный заголовок:

#ifndef MAMAN_IBAZ_H
#define MAMAN_IBAZ_H

#include <glib-object.h>

#define MAMAN_TYPE_IBAZ                (maman_ibaz_get_type ())
#define MAMAN_IBAZ(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_IBAZ, MamanIbaz))
#define MAMAN_IS_IBAZ(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_IBAZ))
#define MAMAN_IBAZ_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_TYPE_IBAZ, MamanIbazInterface))


typedef struct _MamanIbaz MamanIbaz; /* макет объекта */
typedef struct _MamanIbazInterface MamanIbazInterface;

struct _MamanIbazInterface {
  GTypeInterface parent;

  void (*do_action) (MamanIbaz *self);
};

GType maman_ibaz_get_type (void);

void maman_ibaz_do_action (MamanIbaz *self);

#endif /*MAMAN_IBAZ_H*/

Этот код похож на обычную GType которая наследует GObject за исключением некоторых деталей:

  • _GET_CLASS макрос называется _GET_INTERFACE и реализуется не с помощью G_TYPE_INSTANCE_GET_CLASS, а с помощью G_TYPE_INSTANCE_GET_INTERFACE.

  • Тип экземпляра MamanIbaz определён не полностью: он используется просто как абстрактный тип который представляет экземпляр любого объекта реализующего интерфейс.

Реализация самого типа MamanIbaz тривиальна:

  • maman_ibaz_get_type регистрирует тип в системе типов.

  • maman_ibaz_base_init как ожидается регистрирует интерфейсные сигналы если они есть (позже мы рассмотрим как их использовать). Убедитесь что используете статичную локальную логическую переменную чтобы не выполнять код инициализации дважды (как описано в “Interface Initialization”, base_init выполняется один раз для каждой реализованной инстанциации интерфейса)

  • maman_ibaz_do_action разыменовывает структуру класса для доступа к связанным с ней функциям класса и их вызова.

Как определить реализацию Интерфейса?

Как только интерфейс определён, его реализация довольно тривиальна. Исходный код, показывающий как это делается для определённого интерфейса IBaz в предыдущем разделе, расположен в sample/interface/maman-baz.{h|c}.

Первый шаг - определяем обычную GType. Здесь мы решили использовать GType которая наследует GObject. Её имя MamanBaz:

В этом заголовке нет ничего сверхъестественного или страшного: он не определяет никакого особенного API и не наследует особенный тип.

Второй шаг - реализация maman_baz_get_type:

Эта функция очень похожа на подобные функции рассматриваемые нами ранее. Единственный интерфейсно-специфичный код представленный здесь это вызов g_type_add_interface_static который используется для информирования системы типов что зарегистрированный тип GType также реализует интерфейс MAMAN_TYPE_IBAZ.

baz_interface_init, функция инициализации интерфейса, она тоже очень проста:

baz_interface_init просто инициализирует интерфейсные методы для реализации определённого MamanBaz: maman_baz_do_action не делает ничего полезного но могла бы :)

Предпосылки определения Интерфейса

Для определения что интерфейс требует присутствия других интерфейсов при реализации, GObject вводит понятие предпосылки: возможность связать список необходимых как условие интерфейсов с интерфейсом. Например, если объект A хочет реализовать интерфейс I1, и если интерфейс I1 имеет предпосылку на интерфейс I2, A реализует оба интерфейса I1 и I2.

Механизм описанный выше на практике очень похож на Java, интерфейс I1 дополняется интерфейсом I2. Пример ниже показывает GObject эквивалент:

Код показанный выше добавляет интерфейс MamanIbaz в список предпосылок MamanIbar в то время как код ниже показывает как реализовать оба интерфейса и зарегистрировать их реализацию:

Важно обратить внимание что порядок, в котором реализованные интерфейсы добавляются к основному объекту, не случаен: g_type_add_interface_static должна быть вызвана сначала для интерфейсов которые не имеют никаких предпосылок а зтем для остальных.

Полный исходный код, показывающий как определить интерфейс MamanIbar который требует MamanIbaz и как реализовать интерфейс MamanIbar, расположен в sample/interface/maman-ibar.{h|c} и sample/interface/maman-bar.{h|c}.

Свойства интерфейса

Начиная с версии glib 2.4, GObject интерфейсы могут также иметь свойства. Декларация свойств интерфейса похожа на декларацию свойств обычных типов GObject как показано в“Object properties”, исключая то, что g_object_interface_install_property используется для декларации свойств вместо g_object_class_install_property.

Для включения свойства с именем 'name' типа string в код выше приведённого примера интерфейса maman_ibaz, нам нужно добавить только одну[14] строчку в maman_ibaz_base_init[15] как показано ниже:

Одно замечание - задекларированному свойству не был присвоен целочисленный ID. Причина в том, что целочисленный ID этого свойства используется только внутри методов получения и установки, а так как интерфейс не реализует свойства, нет необходимости привязывать целочисленный ID к свойствам интерфейса.

История для конструкторов интерфейса также весьма тривиальна. Конструктор должен задекларировать и определить свойства обычным способом как описано в the section called “Object properties”, за исключением одной мелочи: он должен задекларировать свойства интерфейса используя g_object_class_override_property вместоg_object_class_install_property. Следующий код демонстрирует изменения необходимые в декларации и реализации MamanBaz показанной выше:

[14] Это действительно одна строка расширенная на шесть для ясности

[15] g_object_interface_install_property может также быть вызвана из class_init но её нельзя вызвать после этой точки.

Last updated

Was this helpful?