GObject对象系统(8) 定义和实现接口

How to define and implement interfaces 怎么定义和实现接口

Defining interfaces Implementing interfaces Interface definition prerequisites Interface properties Overriding interface methods

Defining interfaces 定义接口

The theory behind how GObject interfaces work is given in the section called “Non-instantiable classed types: interfaces”; this section covers how to define and implement an interface.

The first step is to get the header right. This interface defines three methods:

被章主要讨论怎样定义和实现一个接口,第一步是编写正确的头文件,本接口定义三个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
 * Copyright/Licensing information.
 */

#ifndef __VIEWER_EDITABLE_H__
#define __VIEWER_EDITABLE_H__

#include <glib-object.h>

G_BEGIN_DECLS

#define VIEWER_TYPE_EDITABLE viewer_editable_get_type ()
G_DECLARE_INTERFACE (ViewerEditable, viewer_editable, VIEWER, EDITABLE, GObject)

struct _ViewerEditableInterface
{
  GTypeInterface parent_iface;

  void (*save) (ViewerEditable  *self,
                GError         **error);
  void (*undo) (ViewerEditable  *self,
                guint            n_steps);
  void (*redo) (ViewerEditable  *self,
                guint            n_steps);
};

void viewer_editable_save (ViewerEditable  *self,
                           GError         **error);
void viewer_editable_undo (ViewerEditable  *self,
                           guint            n_steps);
void viewer_editable_redo (ViewerEditable  *self,
                           guint            n_steps);

G_END_DECLS

#endif /* __VIEWER_EDITABLE_H__ */

This code is the same as the code for a normal GType which derives from a GObject except for a few details:

  • The _GET_CLASS function is called _GET_IFACE (and is defined by G_DECLARE_INTERFACE).

  • The instance type, ViewerEditable, is not fully defined: it is used merely as an abstract type which represents an instance of whatever object which implements the interface.

  • The parent of the ViewerEditableInterface is GTypeInterface, not GObjectClass.

这个代码跟定义一个从GObject派生而来的普通GType类型相似,除了:
* _GET_CLASS函数变成_GET_IFACE函数(由G_DECLARE_INTERFACE定义);
* 实例类型ViewerEditable并没有被完全定义(译者:所以只能作指针使用):它主要用作代表实现了这个接口的任意类型的实例的抽象类型;
* ViewerEditableInterface的父类型是GTypeInterface而不是GObjectClass。

The implementation of the ViewerEditable type itself is trivial:

  • G_DEFINE_INTERFACE creates a viewer_editable_get_type function which registers the type in the type system. The third argument is used to define a prerequisite interface (which we'll talk about more later). Just pass 0 for this argument when an interface has no prerequisite.

  • viewer_editable_default_init is expected to register the interface's signals if there are any (we will see a bit later how to use them).

  • The interface methods viewer_editable_saveviewer_editable_undo and viewer_editable_redo dereference the interface structure to access its associated interface function and call it.

ViewerEditable类型的实现有点繁琐:
* G_DEFINE_INTERFACE创建一个用来在类型系统中注册该类型的viewer_editable_get_type函数。第三个参数用于定义一个预依赖接口(我们将在后面讨论),当不需要预依赖接口时传递0即可;
* 如果需要的话你可以用viewer_editable_default_init函数来注册接口信号(你可以在之后看到怎么使用它);
* 接口方法 viewer_editable_save ,   viewer_editable_undo  和   viewer_editable_redo  解引用接口结构指针来访问相关的接口函数并调用该函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
G_DEFINE_INTERFACE (ViewerEditable, viewer_editable, G_TYPE_OBJECT)

static void
viewer_editable_default_init (ViewerEditableInterface *iface)
{
    /* add properties and signals to the interface here */
}

void
viewer_editable_save (ViewerEditable  *self,
                      GError         **error)
{
  ViewerEditableInterface *iface;

  g_return_if_fail (VIEWER_IS_EDITABLE (self));
  g_return_if_fail (error == NULL || *error == NULL);

  iface = VIEWER_EDITABLE_GET_IFACE (self);
  g_return_if_fail (iface->save != NULL);
  iface->save (self, error);
}

void
viewer_editable_undo (ViewerEditable *self,
                      guint           n_steps)
{
  ViewerEditableInterface *iface;

  g_return_if_fail (VIEWER_IS_EDITABLE (self));

  iface = VIEWER_EDITABLE_GET_IFACE (self);
  g_return_if_fail (iface->undo != NULL);
  iface->undo (self, n_steps);
}

void
viewer_editable_redo (ViewerEditable *self,
                      guint           n_steps)
{
  ViewerEditableInterface *iface;

  g_return_if_fail (VIEWER_IS_EDITABLE (self));

  iface = VIEWER_EDITABLE_GET_IFACE (self);
  g_return_if_fail (iface->redo != NULL);
  iface->redo (self, n_steps);
}




Implementing interfaces 实现接口

Once the interface is defined, implementing it is rather trivial.

一旦接口被定义,实现接口就有点繁琐了。

The first step is to define a normal final GObject class exactly as in the section called “Boilerplate header code”.

第一步是定义一个普通的终类型。

The second step is to implement ViewerFile by defining it using G_DEFINE_TYPE_WITH_CODE and G_IMPLEMENT_INTERFACEinstead of G_DEFINE_TYPE:

第二步是通用使用G_DEFINE_TYPE_WITH_CODE和G_IMPLETENT_INTERFACE而不是G_DEFINE_TYPE来定义类型来实现ViewerFIle。

1
2
3
4
5
static void viewer_file_editable_interface_init (ViewerEditableInterface *iface);

G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
                                                viewer_file_editable_interface_init))

This definition is very much like all the similar functions seen previously. The only interface-specific code present here is the use ofG_IMPLEMENT_INTERFACE.

定义的过程跟之前的很相似,唯一与接口相关的部分是使用了G_IMPLEMENT_INTERFACE宏。

Classes can implement multiple interfaces by using multiple calls to G_IMPLEMENT_INTERFACE inside the call toG_DEFINE_TYPE_WITH_CODE

类可以在G_DEFINE_TYPE_WITH_CODE中使用多个G_IMPLEMENT_INTERFACE的调用来实现多个接口。

viewer_file_editable_interface_init, the interface initialization function: inside it every virtual method of the interface must be assigned to its implementation:

在接口的初始化函数viewer_file_editable_interface_init中每一个接口的虚方法都必须被赋值为它的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static void
viewer_file_editable_save (ViewerFile  *self,
                           GError     **error)
{
  g_print ("File implementation of editable interface save method: %s.\n",
           self->filename);
}

static void
viewer_file_editable_undo (ViewerFile *self,
                           guint       n_steps)
{
  g_print ("File implementation of editable interface undo method: %s.\n",
           self->filename);
}

static void
viewer_file_editable_redo (ViewerFile *self,
                           guint       n_steps)
{
  g_print ("File implementation of editable interface redo method: %s.\n",
           self->filename);
}

static void
viewer_file_editable_interface_init (ViewerEditableInterface *iface)
{
  iface->save = viewer_file_editable_save;
  iface->undo = viewer_file_editable_undo;
  iface->redo = viewer_file_editable_redo;
}

static void
viewer_file_init (ViewerFile *self)
{
  /* Instance variable initialisation code. */
}

If the object is not of final type, e.g. was declared using G_DECLARE_DERIVABLE_TYPE then G_ADD_PRIVATE macro should be added. The private structure should be declared exactly as for a normal derivable object, see the section called “Boilerplate code”.

如果对象不是一个终类型,例如使用了G_DECLARE_DERIVABLE_TYPE声明的可派生类型,那么G_ADD_PRIVATE宏就需要被使用。私有结构要像一个普通的可派生对象一样声明。

1
2
3
4
G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT,
                         G_ADD_PRIVATE (ViewerFile)
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
                                                viewer_file_editable_interface_init))




Interface definition prerequisites 接口定义的预依赖

To specify that an interface requires the presence of other interfaces when implemented, GObject introduces the concept ofprerequisites: it is possible to associate a list of prerequisite types to an interface. For example, if object A wishes to implement interface I1, and if interface I1 has a prerequisite on interface I2, A has to implement both I1 and I2.

要指定在一个接口实现时要求已经实现了另一个接口,GObject提供了接口预依赖的概念:可以为一个接口设置多个预依赖。例如,如果对象A希望实现接口I1,而I1由一个对接口I2的预依赖,A就必须都实现I1和I2。


The mechanism described above is, in practice, very similar to Java's interface I1 extends interface I2. The example below shows the GObject equivalent:

上述方法实践上很类似与Java中接口I1扩展了接口I2,下述例子描述了GObject等同的实现:

1
2
/* Make the ViewerEditableLossy interface require ViewerEditable interface. */
G_DEFINE_INTERFACE (ViewerEditableLossy, viewer_editable_lossy, VIEWER_TYPE_EDITABLE)

In the G_DEFINE_INTERFACE call above, the third parameter defines the prerequisite type. This is the GType of either an interface or a class. In this case the ViewerEditable interface is a prerequisite of ViewerEditableLossy. The code below shows how an implementation can implement both interfaces and register their implementations:

在上述G_DEFINE_INTERFACE调用中,第三个参数定义了预依赖类型。这是一个接口或者一个类型的GType,在这里ViewerEditable是ViewerEditableLossy的预依赖,下述代码展示了怎么同时实现和注册这两个接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
static void
viewer_file_editable_lossy_compress (ViewerEditableLossy *editable)
{
  ViewerFile *self = VIEWER_FILE (editable);

  g_print ("File implementation of lossy editable interface compress method: %s.\n",
           self->filename);
}

static void
viewer_file_editable_lossy_interface_init (ViewerEditableLossyInterface *iface)
{
  iface->compress = viewer_file_editable_lossy_compress;
}

static void
viewer_file_editable_save (ViewerEditable  *editable,
                           GError         **error)
{
  ViewerFile *self = VIEWER_FILE (editable);

  g_print ("File implementation of editable interface save method: %s.\n",
           self->filename);
}

static void
viewer_file_editable_undo (ViewerEditable *editable,
                           guint           n_steps)
{
  ViewerFile *self = VIEWER_FILE (editable);

  g_print ("File implementation of editable interface undo method: %s.\n",
           self->filename);
}

static void
viewer_file_editable_redo (ViewerEditable *editable,
                           guint           n_steps)
{
  ViewerFile *self = VIEWER_FILE (editable);

  g_print ("File implementation of editable interface redo method: %s.\n",
           self->filename);
}

static void
viewer_file_editable_interface_init (ViewerEditableInterface *iface)
{
  iface->save = viewer_file_editable_save;
  iface->undo = viewer_file_editable_undo;
  iface->redo = viewer_file_editable_redo;
}

static void
viewer_file_class_init (ViewerFileClass *klass)
{
  /* Nothing here. */
}

static void
viewer_file_init (ViewerFile *self)
{
  /* Instance variable initialisation code. */
}

G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
                                                viewer_file_editable_interface_init)
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE_LOSSY,
                                                viewer_file_editable_lossy_interface_init))

It is very important to notice that the order in which interface implementations are added to the main object is not random:g_type_add_interface_static, which is called by G_IMPLEMENT_INTERFACE, must be invoked first on the interfaces which have no prerequisites and then on the others.

注意接口实现的顺序不是随机的:被G_IMPLEMENT_INTERFACE所调用的g_type_add_interface_static必须首先对没有任何预依赖的接口调用,之后才能对其他接口调用。






Interface properties 接口属性

GObject interfaces can also have properties. Declaration of the interface properties is similar to declaring the properties of ordinary GObject types as explained in the section called “Object properties”, except that g_object_interface_install_property is used to declare the properties instead of g_object_class_install_property.

GObject接口也可以拥有属性。声明接口的属性跟声明普通GObject类型的属性很相似,除了使用g_object_interface_install_property而不是g_object_class_install_property来声明属性。


To include a property named 'autosave-frequency' of type gdouble in the ViewerEditable interface example code above, we only need to add one call in viewer_editable_default_init as shown below:

要为上述代码的ViewerEditable接口添加一个名为"autosave-frequency",类型为gdouble的属性,我们仅仅需要在viewer_editable_default_init中以如下方式添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
static void
viewer_editable_default_init (ViewerEditableInterface *iface)
{
  g_object_interface_install_property (iface,
                                       g_param_spec_double ("autosave-frequency",
                                                            "Autosave frequency",
                                                            "Frequency (in per-seconds) to autosave backups of the editable content at. "
                                                            "Or zero to disable autosaves.",
                                                            0.0,  /* minimum */
                                                            G_MAXDOUBLE,  /* maximum */
                                                            0.0,  /* default */
                                                            G_PARAM_READWRITE));
}

One point worth noting is that the declared property wasn't assigned an integer ID. The reason being that integer IDs of properties are used only inside the get_property and set_property virtual methods. Since interfaces declare but do not implement properties, there is no need to assign integer IDs to them.

值的注意的一点是属性声明是并没有给它赋值一个整数ID,这样的原因是属性的整数ID只在get_property和set_property虚方法中使用,因为接口只是声明而没有实现属性,所以就没有必要给它们赋值一个整数ID。

An implementation declares and defines its properties in the usual way as explained in the section called “Object properties”, except for one small change: it can declare the properties of the interface it implements using g_object_class_override_propertyinstead of g_object_class_install_property. The following code snippet shows the modifications needed in the ViewerFiledeclaration and implementation above:

声明和实现属性的常用方式就像之前章节解释的那样,除了有一点发生改变:使用g_object_class_override_property而不是g_object_class_install_property来声明对象所实现的接口属性。下述代码片段展示了在ViewerFile上述的声明和实现中所需的修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
struct _ViewerFile
{
  GObject parent_instance;

  gdouble autosave_frequency;
};

enum
{
  PROP_AUTOSAVE_FREQUENCY = 1,
  N_PROPERTIES
};

static void
viewer_file_set_property (GObject      *object,
                          guint         prop_id,
                          const GValue *value,
                          GParamSpec   *pspec)
{
  ViewerFile *file = VIEWER_FILE (object);

  switch (prop_id)
    {
    case PROP_AUTOSAVE_FREQUENCY:
      file->autosave_frequency = g_value_get_double (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
viewer_file_get_property (GObject    *object,
                          guint       prop_id,
                          GValue     *value,
                          GParamSpec *pspec)
{
  ViewerFile *file = VIEWER_FILE (object);

  switch (prop_id)
    {
    case PROP_AUTOSAVE_FREQUENCY:
      g_value_set_double (value, file->autosave_frequency);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
viewer_file_class_init (ViewerFileClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->set_property = viewer_file_set_property;
  object_class->get_property = viewer_file_get_property;

  g_object_class_override_property (object_class, PROP_AUTOSAVE_FREQUENCY, "autosave-frequency");
}



Overriding interface methods 重写接口方法

If a base class already implements an interface and a derived class needs to implement the same interface but needs to override certain methods, you must reimplement the interface and set only the interface methods which need overriding.

如果基类已经实现了一个接口而派生类需要实现该接口并重写部分方法,派生类必须重新实现该接口并仅仅设置需要重写的接口方法。


In this example, ViewerAudioFile is derived from ViewerFile. Both implement the ViewerEditable interface. ViewerAudioFile only implements one method of the ViewerEditable interface and uses the base class implementation of the other.

在这个例子中,ViewerAudioFIle从ViewFIle中派生而来,这两个类型都实现了ViewerEditable接口。ViewerAudioFile仅仅实现了ViewerEditable接口的一个方法并使用了基类对其他接口的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static void
viewer_audio_file_editable_save (ViewerEditable  *editable,
                                 GError         **error)
{
  ViewerAudioFile *self = VIEWER_AUDIO_FILE (editable);

  g_print ("Audio file implementation of editable interface save method.\n");
}

static void
viewer_audio_file_editable_interface_init (ViewerEditableInterface *iface)
{
  /* Override the implementation of save(). */
  iface->save = viewer_audio_file_editable_save;

  /*
   * Leave iface->undo and ->redo alone, they are already set to the
   * base class implementation.
   */
}

G_DEFINE_TYPE_WITH_CODE (ViewerAudioFile, viewer_audio_file, VIEWER_TYPE_FILE,
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
                                                viewer_audio_file_editable_interface_init))

static void
viewer_audio_file_class_init (ViewerAudioFileClass *klass)
{
  /* Nothing here. */
}

static void
viewer_audio_file_init (ViewerAudioFile *self)
{
  /* Nothing here. */
}

To access the base class interface implementation use g_type_interface_peek_parent from within an interface's default_initfunction.

要获取基类接口的实现在接口的default_init函数中使用g_type_interface_peek_parent。


To call the base class implementation of an interface method from an derived class where than interface method has been overridden, stash away the pointer returned from g_type_interface_peek_parent in a global variable.

要在一个接口的方法已经被重写的派生类中调用基类的接口实现,要把从g_type_insterface_peek_parent中返回的指针存储在一个全局变量中。


In this example ViewerAudioFile overrides the save interface method. In its overridden method it calls the base class implementation of the same interface method.

下述例子中ViewerAudioFile重写了save接口方法,在其重写的接口方法中调用了基类对该方法的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static ViewerEditableInterface *viewer_editable_parent_interface = NULL;

static void
viewer_audio_file_editable_save (ViewerEditable  *editable,
                                 GError         **error)
{
  ViewerAudioFile *self = VIEWER_AUDIO_FILE (editable);

  g_print ("Audio file implementation of editable interface save method.\n");

  /* Now call the base implementation */
  viewer_editable_parent_interface->save (editable, error);
}

static void
viewer_audio_file_editable_interface_init (ViewerEditableInterface *iface)
{
  viewer_editable_parent_interface = g_type_interface_peek_parent (iface);

  iface->save = viewer_audio_file_editable_save;
}

G_DEFINE_TYPE_WITH_CODE (ViewerAudioFile, viewer_audio_file, VIEWER_TYPE_FILE,
                         G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
                                                viewer_audio_file_editable_interface_init))

static void
viewer_audio_file_class_init (ViewerAudioFileClass *klass)
{
  /* Nothing here. */
}

static void
viewer_audio_file_init (ViewerAudioFile *self)
{
  /* Nothing here. */
}



转载于:https://my.oschina.net/wsgalaxy/blog/3008513

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值