忽悠一下在C++中使用IoC和DSM (3, 4 )

kjin101 2007-12-18 02:03:44
3. 软件组装及部署描述
在使用IoC框架的C++软件开发过程中,管线模块及管线衔接代码转移并集中至IoC框架内,业务逻辑组件(高层���块或库类)的开发只需关注业务逻辑本身。软件的搭接由IoC容器(或库类)按照用户提供的软件组装及部署描述(assembly and deployment description)完成。因为由分散组件搭接成的软件呈树状或更广义的图状结构,所以,软件组装及部署描述就是对这种树图结构模型的描述。这种描述通常有三种表达(编写)形式,既指令代码(code),元数据(metadata),和用户数据(data)。

虽然经典面向对象语言(比如C++和Java)可以有效地按程序步骤构造树图状数据结构,但对结构整体的“模型描述”却苍白无力。在C++中,要搭建一个树图结构的代码不外乎就是一步步地去调用类似allocNode(),addNode(),wireNodes()等等函数。树图结构模型在这种指令式的(imperative)“步骤描述”中荡然无存。对于管线逻辑比较简单的应用(例如底层或子模块),结构模型并不至关重要甚至是多此一举,而步骤描述或干脆抛弃IoC框架也许更直接了当。然而对于管线逻辑较复杂的应用,对结构搭接具体步骤描述所引入的复杂性则与使用IoC框架抽象管线逻辑的原始初衷背道而驰。

所谓元数据表述就是以编程语言的元数据结构来描述软件的管线结构模型。其实质就是利用传统指令性编程语言中类(class)结构定义的声明式语法来表达管线结构。比如,如果一个结构有10个节点(组件),按此方法就是让用户定义一个包含10个相应成员函数的类(class)并辅助以相应注释(annotation)标识管线连接。进行软件搭接时,IoC框架通过反射机制来解读这个类的结构并将其看做组件搭接的管线结构描述。这种方法,看似提供了一种声明式的(declarative)模型描述,实际上则属于一种牵强附会甚至是生搬硬套的kludge,除了能满足“只使用编程语言本身来表述管线逻辑”这一教义心态之外不具任何正面意义。

IoC 框架中有效灵活自然直观的结构描述形式恰恰就是被大牛们鄙视为恶俗的用户文本数据描述形式。无论大牛们对这种C++和Java语法机制以外的方法如何深恶痛绝,都不得不面对下面一个尴尬的窘境。C++和Java这类被他们(比如红帽Jboss的首席科学家)奉为万能银弹的编程语言中并不提供对树图结构整体具体实例的有效描述手段。因为这类语言的目的仅仅是提供对象类的包装抽象机制,而并不是提供具体多对象系统整体部署结构的模型表述方法(更不要说对各种模型之间变换,甚至变换的变换的声明式描述)。

4. 基于XML的组装及部署描述
主流IoC框架(甚至很多传统非IoC组件框架如EJB2.0和CCM)中用户文本数据形式的组装及部署描述大都是基于XML。XML的设计目的恰恰就是提供对树图结构的声明式描述。对用户来说,XML标准及技术成熟稳定,已被普遍采用和支持(各种XML解析器和工具满天飞)。另外,XML框架内具备完善的声明式结构转换技术(XSLT,XQuery),为从底层通用IoC组件部署描述提高到“针对问题域的特定建模”DSM(domain specific modeling)准备好了理想平台。最重要的是,与SQL类似,XML是种连编程菜鸟都大呼容易的简单直观技术,这就使得很多领域专家能够对组件构成的系统进行搭建和部署。

下面以开源项目PocoCapsule/C++ IoC框架 中的一个具体例子来介绍这个方法。这个例子的完整代码以及文档在PocoCapsule源代码包安装包 中均可找到。
这个例子中所要搭建部署的是一个如下图所示包含定时触发器(tick generator),GPS定位器(gps locator),导航显示器(navigate display)三个组件的GPS系统。

[img=http://www.pocomatic.com/docs/whitepapers/dsm/gps.jpg]gps[/img]

PocoCapsule容器支持所谓POCO(既前面所说的“平庸C++对象”),从而对组件接口模型几乎没有任何限制。这三个组件的基类 TickGen,GPSLocator和NavDisplay都是由用户自己在Interfaces.h 中如下定义的:


class EventListener {
public:
virtual ~EventListener() {}
virtual void refresh() = 0;
};

class EventEmitter {
public:
virtual ~EventEmitter() {}
virtual void subscribe(EventListener*) = 0;
};

class TickGen : public EventEmitter
{
public:
virtual void start() = 0;
};

class GPSLocator : public EventEmitter, public EventListener
{
public:
virtual int get_pos_x() = 0;
virtual int get_pos_y() = 0;
};

class NavDisplay : public EventListener
{
public:
};

这些组件的具体实现类TickGenImpl,GPSLocatorImpl和NavDisplayImpl则大致定义如下(忽略所有与组装不相关细节)。


class TickGenImpl : public TickGen
{
...
public:
TickGenImpl(int cnt, int interval);
...
};

class GPSLocatorImpl : public GPSLocator
{
...
public:
GPSLocatorImpl();
...
};

class NavDisplayImpl : public NavDisplay
{
...
public:
NavDisplayImpl(GPSLocator* loc) ;
...
};

这些组件基类和具体实现类的定义以及它们的编译连接(动态或静态库均可)等开发制作方式与一般C++应用模块没任何区别,完全不需考虑IoC容器。它们甚至可以是IoC时代以前已由第三方制作好,不提供源代码的模块。在这个例子中,除了需要用到这些组件实现类的构造算子原型以外省略掉了其他所有不需要关心的实现细节。

接下来,可以用PocoCapsule的XML语法来描述这个GPS应用。PocoCapsule采用与Spring Framework尽量相近的XML文本格式(schema,定义在poco-application.context.dtd 中),并针对C++语言特征进行了扩充。比如,将bean实例化后的setter调用普遍化为任何IoC调用。在这个文本格式中,一个POCO组件的实例将被声明为一个<bean>元素,包含其构造算子的参数(<method-arg>),实例化后的IoC (<ioc>),以及IoC方法的参量(也是<method-arg>)等子孙元素。比如,这个例子中的GPS应用结构就可以由下面一段XML声明(见setup.xml ):

...
<poco-application-context>
...
<bean class="TickGenImpl" lazy-init="false">
<method-arg type="short" value="10"/>
<method-arg type="short" value="1"/>
<ioc method="subscribe">
<method-arg ref="gps-locator"/>
</ioc>

<ioc method="start"/>
</bean>

<bean id="gps-locator" class="GPSLocatorImpl">
<ioc method="subscribe">
<method-arg ref="nav-display"/>
</ioc>
</bean>

<bean id="nav-display" class="NavDisplayImpl">
<method-arg ref="gps-locator"/>
</bean>
</poco-application-context>

这段XML声明简单直观地表达了三个组件实例以及它们之间的互相衔接结构。可以形象化地将它用相应的C++形式表示如下:

TickGenImpl* tick_gen = new TickGenImpl(10, 1);
tick_gen->subscribe(gps_locator);
tick_gen->start();

...

GPSLocatorImpl* gps_locator = new GPSLocatorImpl;
gps_locator->subscribe(nav_display);

...

NavDisplayImpl* nav_display = new NavDisplayImpl(gps_locator);

虽然这两种描述看似表达同样的概念,但实际上他们有本质区别。C++版本表达的是构造这个应用的具体先后步骤,因此实际上必须重新修改上面C++代码行的次序才能让程序正常编译和工作。而XML版本表达的则是一种结构,而并非构造这个结构的步骤。结构中各<bean>节点的实例化次序与它们在 XML表述中的先后次序无关,而是由IoC容器根据用户声明的节点属性(比如lazy-init的值)以及衔接时依赖关系的先后来决定。

至此,用户仅需要将这个XML描述以文件或字符串形式交给PocoCapsule/C++ IoC容器(既以文件名或XML字符串为参量调用PocoCapsule/C++ IoC库函数,见main.C ),让其自动组装部署所描述的应用。关于PocoCapsule详细的使用及工作机制描述可参阅其入门教材用户手册 ,及代码实例

待续...
...全文
503 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
meiZiNick 2008-05-01
  • 打赏
  • 举报
回复
不知,帮顶
wshcdr 2007-12-20
  • 打赏
  • 举报
回复
MK

3,881

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧