类对象结构型模式之:Composite(组合模式)总结

本文介绍了设计模式中的组合模式,其主要意图是建立部分-整体的层次结构,使得用户可以统一地处理单个对象和组合对象。在AWTK GUI引擎中,组合模式被用于控件的组织,`widget_t`作为组合组件,支持添加、删除子控件等操作,简化了客户端代码。同时,文章强调了如何通过接口设计实现叶子节点和组合节点的一致性,并讨论了模式的适用性和潜在挑战。
摘要由CSDN通过智能技术生成


前言

        软软件设计模式(Design pattern),简称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。--来自百度百科。
        既然设计模式有那么多好处,我们在做程序设计的时候,就应该充分考虑自己需要解决的问题是否有一个设计模式与之相似,尽量使用现有的解决方案来设计程序,避免代码重复或自己考虑不足导致设计缺陷。
        本周五参加了设计模式研讨会,讨论的主题就是组合模式,这篇博文的目的是对组合模式的总结,以及分享一个组合模式的C语言应用例子,希望对想学习这个模式的同学有所帮助。

一、组合模式基础知识总结

1、模式意图

将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

2、参与者

Component
--为组合中的对象声明接口;
--在适当的情况下,实现所有类共有接口的缺省行为;
--声明一个接口用于访问和管理Component的子组件。
Leaf
-- 在组合中表示叶节点对象,叶节点没有子节点;在组合中定义图元对象的行为。
Composite
-- 定义有子部件的那些部件的行为;
--存储子部件;
--在Component接口中实现与子部件有关的操作。
Client
-- 通过Component接口操作组合部件的对象。

3、结构及效果

结构图:

在这里插入图片描述

效果:
1、定义了包含基本对象和组合对象的类层次结构。
2、简化客户代码,客户代码对叶子节点和组合节点的使用方式是一致的。
3、使得更容易增加新类型的组件。
4、使你的设计变得更加一般化。

4、适用性

1、你想表示对象的部分-整体层次结构。
2、你希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象。

二、组合模式在AWKT中的应用

         [AWTK](https://github.com/zlgopen/awtk) 全称 Toolkit AnyWhere,是 ZLG 开发的开源 GUI 引擎,旨在为嵌入式系统、WEB、各种小程序、手机和 PC 打造的通用 GUI 引擎,为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎。
         在AWTK中用到了组合模式,widget_t相当于Component,具体的控件如button是叶子节点,而window相当于Composite。
结构图:

widget_t的定义部分内容如下,类中包含一个包含一个保存子控件的容器对象children:

struct _widget_t {
  /**
   * @property {xy_t} x
   * @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
   * x坐标(相对于父控件的x坐标)。
   */
  xy_t x;
  /**
   * @property {xy_t} y
   * @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
   * y坐标(相对于父控件的y坐标)。
   */
  xy_t y;
  /**
   * @property {wh_t} w
   * @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
   * 宽度。
   */
  wh_t w;
  /**
   * @property {wh_t} h
   * @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
   * 高度。
   */
  wh_t h;
  /**
   * @property {char*} name
   * @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
   * 控件名字。
   */
  char* name;
  /**
   * @property {char*} pointer_cursor
   * @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
   * 鼠标光标图片名称。
   */
  char* pointer_cursor;
  /**
   * @property {char*} tr_text
   * @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
   * 保存用于翻译的字符串。
   */
  char* tr_text;
  ...
  /**
   * @property {darray_t*} children
   * @annotation ["readable"]
   * 全部子控件。
   */
  darray_t* children;
  ...
widget_t在组合模式中应包含的一些接口
管理子控件的接口:添加、删除、查找和获取子控件的接口;
基本操作接口:绘制控件等。
/**
 * @method widget_add_child
 * 加入一个子控件。
 *
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件对象。
 * @param {widget_t*} child 子控件对象。
 *
 * @return {ret_t} 返回RET_OK表示成功,否则表示失败。
 */
ret_t widget_add_child(widget_t* widget, widget_t* child);

/**
 * @method widget_remove_child
 * 移出指定的子控件(并不销毁)。
 *
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件对象。
 * @param {widget_t*} child 子控件对象。
 *
 * @return {ret_t} 返回RET_OK表示成功,否则表示失败。
 */
ret_t widget_remove_child(widget_t* widget, widget_t* child);

/**
 * @method widget_insert_child
 * 插入子控件到指定的位置。
 *
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件对象。
 * @param {uint32_t} index 位置序数(大于等于总个数,则放到最后)。
 * @param {widget_t*} child 子控件对象。
 *
 * @return {ret_t} 返回RET_OK表示成功,否则表示失败。
 */
ret_t widget_insert_child(widget_t* widget, uint32_t index, widget_t* child);

/**
 * @method widget_restack
 * 调整控件在父控件中的位置序数。
 *
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件对象。
 * @param {uint32_t} index 位置序数(大于等于总个数,则放到最后)。
 *
 * @return {ret_t} 返回RET_OK表示成功,否则表示失败。
 */
ret_t widget_restack(widget_t* widget, uint32_t index);

/**
 * @method widget_child
 * 查找指定名称的子控件(同widget_lookup(widget, name, FALSE))。
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件对象。
 * @param {const char*} name 子控件的名称。
 *
 * @return {widget_t*} 子控件或NULL。
 */
widget_t* widget_child(widget_t* widget, const char* name);

/**
 * @method widget_lookup
 * 查找指定名称的子控件(返回第一个)。
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件对象。
 * @param {const char*} name 子控件的名称。
 * @param {bool_t} recursive 是否递归查找全部子控件。
 *
 * @return {widget_t*} 子控件或NULL。
 */
widget_t* widget_lookup(widget_t* widget, const char* name, bool_t recursive);

/**
 * @method widget_lookup_by_type
 * 查找指定类型的子控件(返回第一个)。
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件对象。
 * @param {const char*} type 子控件的名称。
 * @param {bool_t} recursive 是否递归查找全部子控件。
 *
 * @return {widget_t*} 子控件或NULL。
 */
widget_t* widget_lookup_by_type(widget_t* widget, const char* type, bool_t recursive);

ret_t widget_on_paint_background(widget_t* widget, canvas_t* c);
/**
 * @method widget_on_paint_self
 * 绘制自身。
 * @param {widget_t*} widget 控件对象。
 * @param {canvas_t*} c canvas对象。
 *
 * @return {ret_t} 返回。
 */
ret_t widget_on_paint_self(widget_t* widget, canvas_t* c);
/**
 * @method widget_on_paint_children
 * 绘制子控件。
 * @param {widget_t*} widget 控件对象。
 * @param {canvas_t*} c canvas对象。
 *
 * @return {ret_t} 返回。
 */
ret_t widget_on_paint_children(widget_t* widget, canvas_t* c);
/**
 * @method widget_on_paint_border
 * 绘制边框。
 * @param {widget_t*} widget 控件对象。
 * @param {canvas_t*} c canvas对象。
 *
 * @return {ret_t} 返回。
 */
ret_t widget_on_paint_border(widget_t* widget, canvas_t* c);
...

绘制函数的具体实现代码如下:
static ret_t widget_paint_impl(widget_t* widget, canvas_t* c) {
  int32_t ox = widget->x;
  int32_t oy = widget->y;
  uint8_t save_alpha = c->global_alpha;

  if (widget->opacity < TK_OPACITY_ALPHA) {
    canvas_set_global_alpha(c, (widget->opacity * save_alpha) / 0xff);
  }

  if (widget->astyle != NULL) {
    ox += style_get_int(widget->astyle, STYLE_ID_X_OFFSET, 0);
    oy += style_get_int(widget->astyle, STYLE_ID_Y_OFFSET, 0);
  }

  canvas_translate(c, ox, oy);
  widget_on_paint_begin(widget, c);
  widget_on_paint_background(widget, c);
  widget_on_paint_self(widget, c);
  widget_on_paint_children(widget, c);
  widget_on_paint_border(widget, c);
  widget_on_paint_end(widget, c);

  canvas_untranslate(c, ox, oy);
  if (widget->opacity < TK_OPACITY_ALPHA) {
    canvas_set_global_alpha(c, save_alpha);
  }

  widget_on_paint_done(widget, c);

  return RET_OK;
}

从基类的接口实现发现,在组合模式中,叶子节点和组合节点都需要实现以下几个函数完成绘图,如果没有实现,则使用默认实现:
widget_on_paint_begin
widget_on_paint_background
widget_on_paint_self
widget_on_paint_children
widget_on_paint_border
widget_on_paint_end
         对于button这样的叶子控件,没有子节点;window控件包含子节点,在绘制window的时候,也需要递归绘制子控件。

总结

         组合模式中,叶子节点和组合节点,对于客户操作来说是一样的,这简化了客户代码。组合当中定义了容器来存储子节点,方便对已有组合进行扩展。不过,如果需要添加通用功能,需要修改基类及所有子类,这样影响极大。所以,使用组合模式时,component基类的设计需要考虑全面,否则后期增加新功能将会非常困难。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值