Builder模式在AWTK中的应用

本文介绍了AWTK GUI引擎中Builder模式的应用,详细讲解了如何利用Builder模式解析XML文件并构造富文本。XML解析器作为Director,指导Builder对象——xml_builder_t实现富文本的具体构建。通过设置Builder的处理函数,实现了从XML到富文本的转换,展示了Builder模式在复杂对象构建中的灵活性和可扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

         [AWTK](https://github.com/zlgopen/awtk) 全称 Toolkit AnyWhere,是 ZLG 开发的开源 GUI 引擎,旨在为嵌入式系统、WEB、各种小程序、手机和 PC 打造的通用 GUI 引擎,为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎。如果对AWTK感兴趣,非常欢迎你加入AWTK[生态共建计划](https://github.com/zlgopen/awtk/blob/master/docs/awtk_ecology.md)。
        本周五参加了Builder模式研讨会,这篇博文的目的,一是对Builder模式做一个总结,二是分享AWTK是怎么使用Builder模式的。关于Builder模式,之前有分享了一篇博文[《建造者(Builder)模式应用》](https://blog.csdn.net/woody218/article/details/111827587),介绍的是如何通过aidl文件构造json文件,感兴趣的同学可以去看看。

一、Builder模式总结

1、模式意图

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

2、参与者

Builder
-- 为创建一个Product对象的各个部件指定抽象接口。
ConcreteBuilder
-- 实现Builder的接口以构造和装配该产品的各个部件。
-- 提供一个返回产品的接口。 Director
-- 构造一个使用Builder接口的对象
Product
-- 表示被构造的复杂对象。

3、效果

1)它使得你可以改变一个产品的内部表示
-- Builder对象提供给向导器一个构造产品的抽象接口。
2)它将构造代码和表示代码分开
-- 模式通过封装一个复杂对象的创建和表示方式,提高了对象的模块性。
3)它使你可对构造过程进行更精细的控制
-- 模式的产品是在向导器的控制下一步一步构造出来的。

二、Builder模式在AWTK中的应用

        在AWTK中,builder模式的一个应用是:根据xml文件构造一个富文本。在这个应用当中,xml解析器充当Director角色,负责指导富文本的创建过程;rich_text_parser中的xml_builder_t充当ConcreteBuilder角色,负责富文本的实际构造。

1、解析xml文件

1、通过以下接口,创建xml解析器并设置Builder对象,这样就把解析器和builder绑定起来,通过实际设置的builder对象的接口,来执行实际的构建动作。
/**
 * @method xml_parser_create
 *
 * 创建解析器。
 *
 * @return {XmlParser*} 返回parser对象。
 */
XmlParser* xml_parser_create(void);

/**
 * @method xml_parser_set_builder
 *
 * 设置builder。
 *
 * @param {XmlParser*} parser parser对象。
 * @param {XmlBuilder*} builder builder对象。
 *
 * @return {void} 返回无。
 */
void xml_parser_set_builder(XmlParser* parser, XmlBuilder* builder);
2、xml开始解析tag,结束tag、解析文本及处理解析错误;
1)解析tag,获取属性,调用tag的处理函数:builder->on_start(builder, tag, attrs);
2)结束tag,调用tag结束处理函数:builder->on_end(builder, tag);
3)解析文本,调用文本处理函数:builder->on_text(builder, text, length);
4)遇到解析错误时,调用错误处理函数:builder->on_error(builder, line, col, message);
5)解析结束,调用结束处理函数:builder->destroy(builder);
完整源码可以通过李先静老师的AWTK仓库可以阅读(仓库:https://github.com/zlgopen/awtk,awtk/src/xml/子目录)。
static void xml_parser_parse_start_tag(XmlParser* parser) {
  enum _State {
    STAT_NAME,
    STAT_ATTR,
    STAT_END,
  } state = STAT_NAME;

  char* tag_name = NULL;
  const char* start = parser->read_ptr - 1;

  for (; *parser->read_ptr != '\0'; parser->read_ptr++) {
    char c = *parser->read_ptr;

    switch (state) {
      case STAT_NAME: {
        if (isspace(c) || c == '>' || c == '/') {
          tag_name =
              tk_pointer_from_int(xml_parser_strdup(parser, start, parser->read_ptr - start, TRUE));
          state = (c != '>' && c != '/') ? STAT_ATTR : STAT_END;
        }
        break;
      }
      case STAT_ATTR: {
        xml_parser_parse_attrs(parser, '/');
        state = STAT_END;

        break;
      }
      default:
        break;
    }

    if (state == STAT_END) {
      break;
    }
  }

  tag_name = parser->buffer + tk_pointer_to_int(tag_name);
  xml_builder_on_start(parser->builder, tag_name, (const char**)parser->attrs);

  if (parser->read_ptr[0] == '/') {
    xml_builder_on_end(parser->builder, tag_name);
  }

  for (; *parser->read_ptr != '>' && *parser->read_ptr != '\0'; parser->read_ptr++)
    ;

  return;
}

static void xml_parser_parse_end_tag(XmlParser* parser) {
  char* tag_name = NULL;
  const char* start = parser->read_ptr;
  for (; *parser->read_ptr != '\0'; parser->read_ptr++) {
    if (*parser->read_ptr == '>') {
      tag_name = parser->buffer + xml_parser_strdup(parser, start, parser->read_ptr - start, TRUE);
      xml_builder_on_end(parser->builder, tag_name);

      break;
    }
  }

  return;
}

static void xml_parser_on_text(XmlParser* parser) {
  if (parser->text.size > 0) {
    char* start = parser->text.str;
    char* end = parser->text.str + parser->text.size - 1;

    if (parser->trim_text) {
      while (isspace(*start) && *start) {
        start++;
      }

      while (isspace(*end) && end > start) {
        *end = '\0';
        end--;
      }
    }

    if (end >= start) {
      xml_builder_on_text(parser->builder, start, end - start + 1);
    }
  }
}

2、构造富文本文件

1、首先,通过初始化builder的参数,指定每个步骤实际的处理函数。当解析xml文件,到响应步骤时,就会调用这些处理函数,完成响应的处理操作。
static XmlBuilder* builder_init(xml_builder_t* b, const char* font_name, uint16_t font_size,
                                color_t color, align_v_t align_v) {
  int32_t i = 0;
  memset(b, 0x00, sizeof(xml_builder_t));

  b->builder.on_start = xml_rich_text_on_start;
  b->builder.on_end = xml_rich_text_on_end;
  b->builder.on_text = xml_rich_text_on_text;
  b->builder.on_error = xml_rich_text_on_error;
  b->builder.destroy = xml_rich_text_destroy;
  b->font = b->fonts;

  for (i = 0; i < MAX_FONT_LEVEL; i++) {
    rich_text_font_t* iter = b->fonts + i;
    iter->size = font_size;
    iter->color = color;
    iter->align_v = align_v;
    iter->name = font_name == NULL ? NULL : tk_strdup(font_name);
  }
  str_init(&(b->temp), 100);

  return &(b->builder);
}

2、通过处理tag start和text,构造富文本。由于每个步骤的处理思路是相似的,这里仅列举on_start和on_text的处理函数,完整的源码可以通过李先静老师的AWTK仓库可以阅读(仓库:https://github.com/zlgopen/awtk,awtk/src/ext_widgets/rich_text/rich_text_parser.c)。

static void xml_rich_text_on_start(XmlBuilder* thiz, const char* tag, const char** attrs) {
  int32_t i = 0;
  xml_builder_t* b = (xml_builder_t*)thiz;

  if (tk_str_eq(tag, "font")) {
    xml_rich_text_push_font(b);
    b->font->align_v = ALIGN_V_BOTTOM;

    while (attrs[i]) {
      const char* key = attrs[i];
      const char* value = attrs[i + 1];
      if (tk_str_eq(key, "size")) {
        b->font->size = tk_atoi(value);
      } else if (tk_str_eq(key, "color")) {
        b->font->color = color_parse(value);
      } else if (tk_str_eq(key, "align_v")) {
        const key_type_value_t* kv = align_v_type_find(value);
        if (kv != NULL) {
          b->font->align_v = (align_v_t)(kv->value);
        }
      } else if (tk_str_eq(key, "name")) {
        TKMEM_FREE(b->font->name);
        b->font->name = tk_strdup(value);
      }
      i += 2;
    }
  } else if (tk_str_eq(tag, "b")) {
    xml_rich_text_push_font(b);
    b->font->bold = TRUE;
  } else if (tk_str_eq(tag, "i")) {
    xml_rich_text_push_font(b);
    b->font->italic = TRUE;
  } else if (tk_str_eq(tag, "u")) {
    xml_rich_text_push_font(b);
    b->font->underline = TRUE;
  } else if (tk_str_eq(tag, "image")) {
    int32_t w = 0;
    int32_t h = 0;
    int32_t draw_type = IMAGE_DRAW_CENTER;
    const char* image_name = NULL;

    while (attrs[i]) {
      const char* key = attrs[i];
      const char* value = attrs[i + 1];
      if (tk_str_eq(key, "name")) {
        image_name = value;
      } else if (tk_str_eq(key, "w")) {
        w = tk_atoi(value);
      } else if (tk_str_eq(key, "h")) {
        h = tk_atoi(value);
      } else if (tk_str_eq(key, "draw_type")) {
        const key_type_value_t* kv = image_draw_type_find(value);
        if (kv != NULL) {
          draw_type = kv->value;
        }
      }
      i += 2;
    }

    if (image_name != NULL) {
      b->node = rich_text_node_append(b->node, rich_text_image_create(image_name, w, h, draw_type));
    }
  }

  return;
}

static void xml_rich_text_on_text(XmlBuilder* thiz, const char* text, size_t length) {
  xml_builder_t* b = (xml_builder_t*)thiz;
  rich_text_font_t* font = b->font;

  str_decode_xml_entity_with_len(&(b->temp), text, length);
  b->node = rich_text_node_append(b->node, rich_text_text_create(font, b->temp.str));

  return;
}

总结

        Builder模式的思想是把复杂对象的的构建和具体表现分离,如果把这种思想做进一步延伸,可以得到一种新的设计方法:当一件复杂事物的处理可以分割成相对固定的,一系列简单事物的处理过程时,我们就可以把处理过程独立出来,把如何处理每一个单独的简单事物的逻辑从处理流程分离开来,我们就可以得到对同一个复杂事物做出不同的处理方案。设计模式是一种解决问题的通用思路,通过对已有设计模式做延伸思考,可让我们学到更多。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值