Log4c中的接口实现分离 – 以Appender为例

rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyangxk%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

Log4c中的接口实现分离Appender为例

概要

log4c V1.2.1中的appender为例,说明接口实现的分离,及其价值。

 

一, 接口说明

1.      基于句柄(opaque data structure)的操作

struct __log4c_appender;    // opaque data structure

typedef struct __log4c_appender log4c_appender_t; // 以后,所有客户的访问要以此类型作为句柄,通过提供的接口进行操作。(针对这种opaque data structure,只能定义这些类型的指针,不能定义值,因为定义值需要知道结构体的确切大小和fieldlayout状况)

可进行的操作如下:

// appender的创建和销毁接口

LOG4C_API log4c_appender_t* log4c_appender_get(

const char* a_name);

LOG4C_API log4c_appender_t* log4c_appender_new(

const char* a_name);

LOG4C_API void log4c_appender_delete(

log4c_appender_t* a_appender);

 

// appender的各种accessor

LOG4C_API const char* log4c_appender_get_name(

const log4c_appender_t* a_appender);

LOG4C_API const log4c_appender_type_t* log4c_appender_get_type(

const log4c_appender_t* a_appender);

LOG4C_API const log4c_layout_t* log4c_appender_get_layout(

const log4c_appender_t* a_appender);

LOG4C_API void* log4c_appender_get_udata(

const log4c_appender_t* a_appender);

LOG4C_API const log4c_appender_type_t* log4c_appender_set_type(

log4c_appender_t* a_appender,

const log4c_appender_type_t* a_type);

LOG4C_API void* log4c_appender_set_udata(

log4c_appender_t* a_appender, void* a_udata);

LOG4C_API const log4c_layout_t* log4c_appender_set_layout(

    log4c_appender_t* a_appender,

    const log4c_layout_t* a_layout);

 

// appender的操作,这些操作可以由log4c_appender_set_type进行改变

LOG4C_API int log4c_appender_open(log4c_appender_t* a_appender);

LOG4C_API int log4c_appender_append(

    log4c_appender_t* a_appender,

    log4c_logging_event_t* a_event);

LOG4C_API int log4c_appender_close(

log4c_appender_t* a_appender);

// debug用接口

LOG4C_API void log4c_appender_print(

const log4c_appender_t* a_appender, FILE* a_stream);

LOG4C_API void log4c_appender_types_print(FILE *fp);

 

2.      可定制的操作

typedef struct log4c_appender_type {

    const char*   name;

    int (*open)   (log4c_appender_t*);

    int (*append) (log4c_appender_t*, const log4c_logging_event_t*);

    int (*close)  (log4c_appender_t*);

} log4c_appender_type_t;

所有appender可定制操作都封装到了该结构体中。 rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cyangxk%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml"> 值得注意的是三个函数的参数的类型是log4c_appender_t的指针,也就是说,这些函数被调用时,log4c_appender_type的对象未被作为参数传入。需要时可由log4c_appender_get_type函数来得到。结构体抽象了打开关闭和append操作。针对该结构体,有如下两个操作

// appender_type的操作,将所有appender_type做成hash

LOG4C_API const log4c_appender_type_t* log4c_appender_type_get(

const char* a_name);

LOG4C_API const log4c_appender_type_t* log4c_appender_type_set(

const log4c_appender_type_t* a_type);

 

3.      接口描述

A.      创建,由log4c_appender_new进行创建。

B.      改变行为,通过各种accessor来改变appender的默认行为。特别是log4c_appender_set_type来改变行为, log4c_appender_set_udata来设置type所需的contextlog4c_appender_set_layout来改变layout

C.      所用的模式。可以认为是strategy模式。Appendercontexttypelayout属于strategy

 

二, Appender的实现

1.      句柄结构体的定义。

一接口说明中只是定义了appender所提供的接口,及句柄的未完全定义。句柄的完全定义如下:

struct __log4c_appender

{

  char*                         app_name;

  const log4c_layout_t*            app_layout;

  const log4c_appender_type_t*      app_type;

  int                              app_isopen;

  void*                         app_udata;

};

各种accessor所操作的对象都能在该结构体中找到相应的对象。

2.      app_udata,见好的设计思想总结的3

3.      接口的实现

有了以上的定义,接口的实现是不言自明。

 

三, Appender的使用步骤

1.      读取配置文件。

2.      根据appender的名字,调用log4c_appender_get获得或创建appender对象。

3.      如果有appender type,从全局的appender type hash中获取appende type,并设定到appender 中。

4.      如果有appender layout,从全局的appender layout hash中获取layout,并设定到appender中。

5.      在调用log4c_appender_append时,如果还没有open,首先调用appender typeopen函数,并进行append操作。

四, mmap type的实现

1.      定义log4c_appender_type的实例,设置名字及三个函数指针。

2.      调用log4c_appender_type_set函数,向log4c注册该mmap type可用。

3.      open函数中,创建udata,并设置所需要的信息后,调用log4c_appender_set_udata设置到appender对象中。

4.      close函数释放udata占用的内存资源和OS资源。

五, stream typesyslog type的实现同mmap基本一样。

六, 好的设计思想总结

1.      基于句柄的接口,做到了最大化的信息隐藏。除了该句柄和提供的接口,客户看不到实现的任何细节。这样实现可以任意的改变,只要不改变接口本身。Windows平台的CreateFileAPI都是基于这样的设计理念。

2.      策略模式。Open,close,append三个操作随不同的appender type而有不同的行为。所以将操作抽象成为函数指针。并将这三个操作进一步抽象为appender type构造体。这样将复杂度分离了。避免了大而复杂的对象。这里函数指针在设计中体现出相当重要的价值。(OO的话,就是抽象基类或接口类)。意识到这种设计的好处并不困难,而真正的困难在于能够正确的提取出这样的函数指针(接口类)来隔离关注点,隔离复杂度,形成统一的抽象。幸运的是,我们不需要第一次就能够正确的分离这些关注点,我们可以渐进的修改我们的设计,直到我们认为OK为止。构建一个prototype,待我们真正的理解了问题之后,扔掉prototype,重新再做一次设计。

3.      原来我认为app_udata只是由appender type对象访问,为什么不直接放在appender type对象中,而偏偏要放在appender对象中。后来我明白了,因为appender type对象是一个服务提供者,该服务提供者可以被任何appender对象所使用。所以除了name,及三个函数之外,appender type对象不能包含特定与任何appender对象的信息。所以,如果需要与appender对象关联的特定信息时,作为解决方案,是把该信息放在appender对象中,而在appender type对象可以在必要的时候可以访问与appender对象关联的特定信息。这就是app_udata及其accessor出现的原因。(个人猜测)

4.      源代码的组织。在头文件中只放客户希望看到的接口,不能在头文件中出现实现细节相关的任何信息。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值