契约式设计在类和单元测试中的应用

发表于:2022-4-15 10:00

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:KunQAQ    来源:稀土掘金

分享:
  契约式设计的定义
  契约式设计(Design by Contract (DbC)),这种设计方式和商业契约的情况类似。契约作用于两方,每一方都会完成一些任务,从而促成契约的达成,但同时,每一方也会接受一些义务,作为制定契约的前提,有任意一方无视了必尽义的义务,则契约失败。
  契约式设计的原则
  ·区分命令和查询。查询返回一个结果,但不改变对象的可见性质。命令改变对象的状态,但不一定返回结果。
  · 将基本查询和派生查询分开。派生查询可以用基本查询来定义。
  · 设定一个后验条件,使用一个或多个基本查询的结果来定义每个派生查询。这样我们只要知道基本查询的值,也就能知道派生查询的值。
  例子
  上述原则多用于对类进行设计,命令选择使用set函数设置,而查询选择直接通过对象获取。
  通过URL类的接口函数作为例子:
  typedef int bool;
  #define true 1
  #define false 0
  /* 包含url各个部分的结构体。 */
  typedef struct _url_t {
    char* protocol; /* 协议 */
    char* domain;   /* 域名 */
    int port;       /* 端口号 */
    char* vdir;     /* 虚拟目录 */
    char* filename; /* 文件名 */
    int para_num;   /* 参数数量 */
    char** para;    /* 参数 */
    char* anchor;   /* 锚 */
  } url_t;
  /**
   * @method url_create
   * 创建url结构体。(需要销毁url结构体请调用url_destroy函数)
   *
   * @return {url_t*} 返回url结构体指针(若无内存创建,返回值为空)。
   */
  url_t* url_create(void);
  /**
   * @method url_parse
   * 将url字符串解析为各个部分。
   * @param {const char*} str_url 需解析的url字符串。
   * @param {url_t*} url 已创建的url指针。
   *
   * @return {bool} 若返回true,则url字符串解析成功,否则为失败。
   */
  bool url_parse(const char* str_url, url_t* url);
  /**
   * @method url_set_protocol
   * 设置url结构体的protocol(协议)成员。
   * @param {url_t*} url 已创建的url指针。
   * @param {const char*} protocol 协议。
   *
   * @return {bool} 若返回true,则url的protocol成员设置成功,否则为失败。
   */
  bool url_set_protocol(url_t* url, const char* protocol);
  /**
   * @method url_set_domain
   * 设置url结构体的domain(域名)成员。
   * @param {url_t*} url 已创建的url指针。
   * @param {const char*} domain 域名。
   *
   * @return {bool} 若返回true,则url的domain成员设置成功,否则为失败。
   */
  bool url_set_domain(url_t* url, const char* domain);
  /**
   * @method url_set_port
   * 设置url结构体的port(端口号)成员。
   * @param {url_t*} url 已创建的url指针。
   * @param {int} port 端口号。
   *
   * @return {bool} 若返回true,则url的port成员设置成功,否则为失败。
   */
  bool url_set_port(url_t* url, int port);
  /**
   * @method url_set_vdir
   * 设置url结构体的vdir(虚拟目录)成员。
   * @param {url_t*} url 已创建的url指针。
   * @param {const char*} vdir 虚拟目录。
   *
   * @return {bool} 若返回true,则url的vdir成员设置成功,否则为失败。
   */
  bool url_set_vdir(url_t* url, const char* vdir);
  /**
   * @method url_set_filename
   * 设置url结构体的filename(文件名)成员。
   * @param {url_t*} url 已创建的url指针。
   * @param {const char*} filename 文件名。
   *
   * @return {bool} 若返回true,则url的filename成员设置成功,否则为失败。
   */
  bool url_set_filename(url_t* url, const char* filename);
  /**
   * @method url_set_anchor
   * 设置url结构体的anchor(锚)成员。
   * @param {url_t*} url 已创建的url指针。
   * @param {const char*} anchor 锚。
   *
   * @return {bool} 若返回true,则url的anchor成员设置成功,否则为失败。
   */
  bool url_set_anchor(url_t* url, const char* anchor);
  /**
   * @method url_set_para
   * 设置url结构体的para(参数)成员。
   * @param {url_t*} url 已创建的url指针。
   * @param {const char*} para 参数。
   * @param {int16_t} para_index 参数位置(para_index等于0为第一个参数,若para_index大于等于参数的个数,则会新增一个参数)。
   *
   * @return {bool} 若返回true,则url的para成员设置成功,否则为失败。
   */
  bool url_set_para(url_t* url, const char* para, int16_t para_index);
  /**
   * @method url_destroy
   * 将url结构体销毁。
   * @param {url_t*} url 已创建的url指针。
   */
  void url_destroy(url_t* url);

  契约式设计的约束条件
  ·前置条件(precondition):在执行函数体前,对参数进行检查,当参数无误后,再执行函数体。
  · 后置条件(postcondition):在函数体执行后,对函数体返回的结果进行检查。
  · 类不变项(class invariant):在函数体执行前后,对不会发生改变的条件进行检查。
  例子
  因为用在实际程序中会降低效率,所以上述约束条件多用于单元测试
  通过URL类的分析函数测试作为例子:
  #define TEST_URL "https://www.kun.com:443/kun/dir/KUN?1&2#name"
  TEST(UrlParse, UrlParse) {
    char* str = TEST_URL;
    url_t* url = url_create();
    /* 先验条件 */
    ASSERT_NE(nullptr, url);       /* 类不变项 */
    /***********/
    bool ret = url_parse(str, url);  /* 函数体 */
    /* 后验条件 */
    ASSERT_EQ(true, ret);
    ASSERT_NE(nullptr, url);       /* 类不变项 */
    /***********/
    ASSERT_STREQ("https", url->protocol);
    ASSERT_STREQ("www.kun.com", url->domain);
    ASSERT_EQ(443, url->port);
    ASSERT_STREQ("kun/dir", url->vdir);
    ASSERT_STREQ("KUN", url->filename);
    ASSERT_EQ(2, url->para_num);
    ASSERT_STREQ("1", url->para[0]);
    ASSERT_STREQ("2", url->para[1]);
    ASSERT_STREQ("name", url->anchor);
    url_destroy(url);
  }

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
价值398元的测试课程免费赠送,填问卷领取吧!

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计

法律顾问:上海漕溪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2022
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号