第02课:入门篇——漫谈 CTK

十万个为什么

五千个在哪里?七千个怎么办?十万个为什么?……生活中,有很多奥秘在等着我们去思考、揭示!

同样地,在使用 CTK 时,很多小伙伴一定也存在诸多疑问:

  • 为什么 CTK Plugin Framework 要借鉴 OSGi?
  • 为什么 CTK 要基于 Qt 实现?
  • CTK Plugin Framework 的架构策略是什么?
  • 使用 CTK Plugin Framework 的好处是什么?
  • 为什么 CTK 不流行?

针对这些问题,我们来一探究竟!

为什么 CTK Plugin Framework 要借鉴 OSGi?

对于任何新框架/库,在设计之初都需要考虑众多因素,CTK Plugin Framework 也不例外:

  • 框架不得对插件强加功能限制;
  • 插件应该通过定义良好的方式(服务、接口等)进行通信;
  • 必须处理插件之间的依赖关系;
  • 应该在运行时加载插件;
  • ……

看似每一项任务都极具挑战,该如何应对呢?调研!!!这应该是每一个技术人首先想到的,因为它是最快、最直接的办法,仔细研究已有方案是非常有益的。就这样,CTK 小组研究了一系列面向服务的设计,其中比较出名的有:

  • CORBA:由 OMG 组织制订的一种标准的面向对象应用程序体系规范;
  • OSGi:Java 动态化模块化系统的一系列规范;
  • ……

一开始,CORBA 看起来挺有意思,但是对 CTK 的目标用户来说,会带来太多的开销并增加很多复杂性。此外,也有一些其他解决方案也能够很好地处理可扩展性,但是它们通常需要实现一些特殊的接口,这在一般情况下不太合适。

OSGi 规范的核心部分是一个框架,其中定义了应用程序的生命周期模式和服务注册。基于这个框架定义了大量的 OSGi 服务:日志、配置管理、偏好,HTTP(运行 servlet)、XML 分析、设备访问、软件包管理、许可管理、星级、用户管理、IO 连接、连线管理、Jini 和 UPnP。由于 OSGi 的可靠性和可扩展性,很多大规模和分布式应用程序都基于它来构建,例如:IDE(Eclipse)、应用服务器(GlassFish、IBM Websphere、Oracle/BEA Weblogic、Jonas、JBoss)、应用框架(Spring、Guice)等。

因此,在某种程度上,像 OSGi 这样的通用插件框架最适合作为 CTK Plugin Framework 的指导方案。能从 OSGi 社区获得的 10 多年经验中受益,谁能不心动呢?

PS: 对于 OSGi 开发人员来说,理解 CTK Plugin Framework 的 API 相对容易。

为什么 CTK 要基于 Qt 实现?

在使用 CTK 时,总能听到质疑的声音:“如果不用 Qt,CTK 一定会很火!”

毛主席曾说:没有调查,没有发言权!

无需过多争论,用事实说话!看看究竟是什么原因,才使得 CTK 基于 Qt 来实现?

  • CTK 需要为生物医学成像应用提供一系列 Widgets。

在 CTK 设计之初,它的目标之一就是为所在领域提供一系列控件。在 C++ 中要找一个高效、跨平台的 GUI 库,我想没有比 Qt 更合适的!

  • Qt Creator 的可扩展性。

Qt Creator 通过一种简单、优雅的方式来实现可扩展性,它使用一个通用的 QObject 池来实现某些可用的接口。同时,通过使用嵌入式文本文件(.pluginspec 文件)来向插件添加元数据(例如:Name、Version 等)。

其实严格来说,CTK Plugin Framework 同时借鉴了 OSGi 和 Qt Creator 的思想。

  • Qt 提供了 Qt Plugin System 和 Qt Service Framework。

Qt Plugin System 提供了两套用于创建插件的 API,高级 API 用于扩展 Qt 本身(例如:自定义数据库驱动、图像格式、文本编解码、自定义样式等),低级 API 用于扩展 Qt 应用程序。

对于 Qt Service Framework 来说,它能使服务的开发和访问方式变得更加容易。Qt 服务提供者可以与特定于平台的服务进行交互,而无需向客户端公开平台的细节。每个服务都通过 QObject 指针公开,这意味着客户端可以通过 Qt MetaObject 系统与服务对象进行交互。

CTK Plugin Framework 的架构策略是什么?

CTK Plugin Framework 是基于 Qt Plugin System 和 Qt Service Framework 实现的,并且它还添加了以下特性来增强这两个系统:

  • 插件元数据(由 MANIFEST.MF 文件提供);
  • 一个定义良好的插件生命周期和上下文;
  • 综合服务发现和注册;
  • ……

注意: 在 Qt Plugin System 中,插件的元数据由 JSON 文件提供。

CTK Plugin Framework 的核心架构主要包含两个组件:Plugin System 本身和 Service Registry。然而,这两个组件是相互关联的,它们在 API 级别上的组合使得系统更加全面、灵活。

  • Plugin System

CTK Core 依赖于 QtCore 模块,因此 CTK Plugin Framework 基于 Qt Plugin System。Qt API 允许在运行时加载和卸载插件,这个功能在 CTK Plugin Framework 中得到了加强,以支持透明化延迟加载和解决依赖关系。

插件的元数据被编译进插件内部,可以通过 API 进行提取。此外,插件系统还使用 SQLite 缓存了元数据,以避免应用程序加载时间问题。另外,Plugin System 支持通过中央注册中心使用服务。

  • Service Registry

Qt Service Framework 是 Qt Mobility 项目发布的一个 Qt 解决方案,这种服务框架允许“声明式服务”(Getting Started with OSGi: Introducing Declarative Services )和按需加载服务实现。为了启用动态(非持久性)服务,Qt Mobility 服务框架可以与 Service Registry 一起使用,类似于 OSGi Core Specifications 中描述的一样。

使用 CTK Plugin Framework 的好处是什么?

由于 CTK Plugin Framework 基于 OSGi,因此它继承了一种非常成熟且完全设计的组件系统,这在 Java 中用于构建高度复杂的应用程序,它将这些好处带给了本地(基于 Qt 的)C++ 应用程序。以下内容摘自 Benefits of Using OSGi,并适应于 CTK Plugin Framework:

  • 降低复杂性

使用 CTK Plugin Framework 开发意味着开发插件,它们隐藏了内部实现,并通过定义良好的服务来和其它插件通信。隐藏内部机制意味着以后可以自由地更改实现,这不仅有助于 Bug 数量的减少,还使得插件的开发变得更加简单,因为只需要实现已经定义好的一定数量的功能接口即可。

  • 复用

标准化的组件模型,使得在应用程序中使用第三方组件变得非常容易。

  • 现实情况

CTK Plugin Framework 是一个动态框架,它可以动态地更新插件和服务。在现实世界中,有很多场景都和动态服务模型相匹配。因此,应用程序可以在其所属的领域中重用 Service Registry 的强大基元(注册、获取、用富有表现力的过滤语言列表、等待服务的出现和消失)。这不仅节省了编写代码,还提供了全局可见性、调试工具以及比为专用解决方案实现的更多的功能。在这样的动态环境下编写代码听起来似乎是个噩梦,但幸运的是,有支持类和框架可以消除大部分(如果不是全部的话)痛苦。

  • 开发简单

CTK Plugin Framework 不仅仅是组件的标准,它还指定了如何安装和管理组件。这个 API 可以被插件用来提供一个管理代理,这个管理代理可以非常简单,如命令 shell、图形桌面应用程序、Amazon EC2 的云计算接口、或 IBM Tivoli 管理系统。标准化的管理 API 使得在现有和未来的系统中集成 CTK Plugin Framework 变得非常容易。

  • 动态更新

OSGi 组件模型是一个动态模型,插件可以在不关闭整个系统的情况下被安装、启动、停止、更新和卸载。

  • 自适应

OSGi 组件模型是从头设计的,以允许组件的混合和匹配。这就要求必须指定组件的依赖关系,并且需要组件在其可选依赖性并不总是可用的环境中生存。Service Registry 是一个动态注册表,其中插件可以注册、获取和监听服务。这种动态服务模型允许插件找出系统中可用的功能,并调整它们所能提供的功能。这使得代码更加灵活,并且能够更好地适应变化。

  • 透明性

插件和服务是 CTK 插件环境中的一等公民。管理 API 提供了对插件的内部状态的访问,以及插件之间的连接方式。可以停止部分应用程序来调试某个问题,或者可以引入诊断插件。

  • 版本控制

在 CTK Plugin Framework 中,所有的插件都经过严格的版本控制,只有能够协作的插件才会被连接在一起。

  • 简单

CTK 插件相关的 API 非常简单,核心 API 不到 25 个类。这个核心 API 足以编写插件、安装、启动、停止、更新和卸载它们,并且还包含了所有的监听类。

  • 懒加载

懒加载是软件中一个很好的点,OSGi 技术有很多的机制来保证只有当类真正需要的时候才开始加载它们。例如,插件可以用饿汉式启动,但是也可以被配置为仅当其它插件使用它们时才启动。服务可以被注册,但只有在使用时才创建。这些懒加载场景,可以节省大量的运行时成本。

  • 非独占性

CTK Plugin Framework 不会接管整个应用程序,你可以选择性地将所提供的功能暴露给应用程序的某些部分,或者甚至可以在同一个进程中运行该框架的多个实例。

  • 非侵入

在一个 CTK 插件环境中,不同插件均有自己的环境。它们可以使用任何设施,框架对此并无限制。CTK 服务没有特殊的接口需求,每个 QObject 都可以作为一个服务,每个类(也包括非 QObject)都可以作为一个接口。

为什么 CTK 不流行?

这是一个很值得深思的问题!既然 CTK Plugin Framework 借鉴了 OSGi 的思想,那么为什么 OSGi 很火,而 CTK 却鲜为人知呢?

  • 只针对医学成像和建模领域?

如果单看 CTK 简介而不做深入了解,也许会存在这样的误区。起初,CTK 的确为医学成像和建模领域准备,但并不仅限于此。作为 CTK 的核心,CTK Plugin Framework 实现了几乎完整的 OSGi 框架 API,所以只要有模块化需求,完全可以在任何 C++ 项目中使用 CTK。

  • 重量级、复杂?

CTK 包含的内容很多,Plugin Framework、Widgets、DICOM……以至于很多人认为它过于复杂、臃肿,便将其拒之门外。当然,CTK 中提供的模块有一定历史原因,因为在设计之初 CTK 针对医学成像和建模领域,其所提供的所有模块都在该领域中必不可少。

然而,这并不影响使用。如果愿意,完全可以选择对自己有用的模块。这也是我选择 CTK 的原因,因为我觉得 CTK Plugin Framework 超级棒!无论是使用框架本身,还是学习它的思想,对自己都大有益处。此外,CTK Widgets 模块还提供了各种各样自定义的炫酷部件(例如:ctkPopupWidget),简直不能太完美!

  • 缺少社区宣传

像 OSGi 这样的大型系统,除了强大的 OSGi 官网 作支撑之外,还有像 OSGi 中文社区 之类的活跃社区进行宣传,以促进 Java 模块化开发技术传播。此外,还要感谢很多大型公司和产品(例如:IBM 和 Eclipse),因为他们也在大力发展 Java 模块化,而这一切都离不开 OSGi。

但是 CTK 除了 CTK 官网CTK GitHub 之外,甚至几乎找不到任何和 CTK 相关的网站,更何况是社区了!

  • 资料匮乏

OSGi 资料多吗?搜下关键字你就知道了!

在调研 CTK 时,我曾翻阅过无数资料,打算尽快上手。但遗憾的是,网上几乎找不到任何相关信息,就连官网的资料也寥寥无几。毫不夸张的说,CTK 的资料约等于 0,所以很多时候不得不去参考 OSGi。

  • 人们更习惯于传统的开发模式

要使用 CTK,除了需要花费相应的学习成本之外,现有代码的更改也不可避免,这对开发人员来说并不具有吸引力。虽然改变意味着新生,但也伴随着痛苦与煎熬。探索未知领域,去做不熟悉的事,不是谁都可以接受的,这不仅需要勇气,还需要决心。

以上的这些问题也是 CTK 面临的一些挑战,后续是否会制定相应的发展战略来应对呢?我们不得而知,拭目以待吧!

PS: 也正因如此,才有此系列文章的诞生,希望它能够帮助更多的人了解 C++ 模块化。

微信扫描登录
关注提示×
扫码关注公众号,获得课程更新动态!