游戏开发论坛

 找回密码
 立即注册
搜索
查看: 1718|回复: 1

一个机器人的多接口适配

[复制链接]

3

主题

137

帖子

151

积分

注册会员

Rank: 2

积分
151
QQ
发表于 2011-10-18 11:34:00 | 显示全部楼层 |阅读模式
[em2] [em2]



        阿西莫夫的科幻小说,想必很多人都看过,尤其是机器人系列给我的印象相当深刻。不管是机器人三定律,类人机器人,还是那个样子虽丑却具有读心术的机器人,都令我感叹作者的惊人想象力啊。
        造一个机器人对我来讲,目前无异于天方夜谭,但是设计一个虚拟机器人应该还是有可能的。好吧,我非常想要一个能和我聊天的机器人,于是我设计一个机器人class RobotTalker,它可能是从某个Robot类继承而来,但,先不管那么多。我只是希望这个机器人能同我正常聊天,以排除宅男的孤独,给我一种存在感。
        我对虚拟机器人有一个特殊的需求,就是要求它能说多种语言,有时可以用普通话和我聊天,有时也能用家乡话给父母解解闷,有时可以在网络上跟国外的程序员们交流一下技术,权当是个翻译。但我只会中文,我自己只能研发出它的中文语言模块。我给RobotTalker提供的随机附带默认的语言接口是DefaultCompany.h:

#ifndef Default_Interface_h
#define Default_Interface_h

void SpeakInChinese(); //函数的具体实现可能在另外一个.cpp文件中,也有可能在一个单独的dll中

#endif

        在我发布了我要做聊天机器人的信息后,收到两个网友的来信,有意为我提供英语模块和俄语模块,他们的接口采取了和我默认接口同样的形式。

CompanyEngland.h 中提供的是英国英语接口:
#ifndef Company_England_h
#define Company_England_h

void SpeakInEnglishUK();

#endif

CompanyRussia.h 提供的是俄语接口。
#ifndef Company_Russia_h
#define Company_Russia_h

void SpeakInRussian();

#endif

同样,他们的具体实现都是以dll形式提供的。

不过,这两个网友给我提了一个交换条件,就是我的机器人做好之后,他们也要一份拷贝,而且他们只想要自己的语言版本。这下可难住我了,看来我得想办法针对我们三个人不同的需求来适配接口。首先,为我们三个人的语言模块分别分配一个厂商代码,根据CompanyCode来决定Robot的实现版本。


在刚开始没有思路的情况下,我先使用传统的C语言形式设计了如下的RobotTalker。
class RobotTalker
{
public:
    RobotTalker(){};
    virtual ~RobotTalker(){};

    void SpeakLanguage();
};

//函数实现:根据宏定义,编译不同的接口。
void RobotTalker::SpeakLanguage()
{
#ifdef DefaultCompanyCode
    SpeakInChinese();
#endif

#ifdef CompanyRussiaCode
    SpeakInRussian();
#endif

#ifdef ComanyEnglandCode
    SpeakInEnglishUK();
#endif
}
依赖于宏编译出不同版本的这种实现形式,不管看起来怎么别扭,但至少已经实现了我们三个人的需求。不过我不敢把这种代码发布给我的客户,在一个宏起作用的情况下,另外两个语言接口是不会被编译的,而且我每次发布代码都要更换宏定义,编译三次啊。

有没有能一劳永逸的方法呢?C++的模板是代替这种宏编译的一种不错的形式。我给机器人设计一个指定类型作为模板参数的模板函数,我的中文模块接口作为模板的一般形式,而英语和俄语作为模板的特化版本,这样客户代码调用机器人代码时,只需要把CompanyCode作为模板参数传递给RobotTalker就可以了。

首先看机器人的语言接口如何实现:

#ifndef Robot_Reader_h
#define Robot_Reader_h

#include "DefaultCompany.h"
#include "CompanyEngland.h"
#include "CompanyRussia.h"

//厂商代码
const int DefaultCompanyCode = 1;
const int CompanyEnglandCode = 100;
const int CompanyRussiaCode = 200;


//聊天机器人
class RobotTalker
{
public:
    RobotTalker(){};
    virtual ~RobotTalker(){};

    template<int CompanyCode> void SpeakLanguage();
};

//函数实现的一般版本默认朗读语言是中文
template<int CompanyCode>
void RobotTalker::SpeakLanguage()
{
    SpeakInChinese();
}

//英语特化版本
template<>
void RobotTalker::SpeakLanguage<CompanyEnglandCode>()
{
    SpeakInEnglishUK();
}

//俄语特化版本
template<>
void RobotTalker::SpeakLanguage<CompanyRussiaCode>()
{
    SpeakInRussian();
}

#endif

我的机器人代码只需要一套,所有的接口时时刻刻都处于编译器的检查范围,也就是说,所有的厂商接口都会始终被编译。而且,只使用英语模块的客户,另外两个接口根本不会编译为可执行代码,不会占用多余的内存空间,和宏一样的效果。
客户要使用这个RobotTalker实现自己定义语言的机器人时,那就很方便了。
#include "RobotTalker.h"

int main()
{
    RobotTalker robot;
    robot.SpeakLanguage<CompanyEnglandCode>(); //讲英语
robot.SpeakLanguage< DefaultCompanyCode>(); //讲中文

RobotTalker tRobotRussian;
robot.SpeakLanguage< CompanyRussiaCode>(); //讲俄语
}

        本文只针对多厂商接口(而且接口格式相同)这个需求展开了讨论,目的是推介一下C++ Template的威力,至于要实现不同功能的机器人之类的话题,那是设计模式的范畴,就不多嘴了。另外针对上述需求,还有一种实现方式,就是根据厂商代码使用不同厂商的接口函数,初始化一个函数指针,客户代码调用时设定自己的厂商代码,调用的函数指针自然就是自己的接口函数。但我总觉得函数模板的形式更优雅一些。

6

主题

74

帖子

230

积分

中级会员

Rank: 3Rank: 3

积分
230
发表于 2011-10-25 16:34:00 | 显示全部楼层

Re:一个机器人的多接口适配

说了半天,就是插件的调用方法。

模板还是宏本质都是编译时决定的,要换语言必须重新编译。这2个没有显著区别。

你可以这么用
template<int code>robot;

template<>robot<EN>{
//实现英文
...
};
//其他语言...

const int code=EN;//改这个就换语言了

typedef robot<code> Robot;

void main(){
Robot r;
r.xxx();
}

常用的插件办法都是用接口实现。

//一个更好的办法,使用接口来定义Robot.

struct RobotBase{
virtual void talk()=0;
virtual ~RobotBase()=0;
};
RobotBase::~RobotBase(){}

//enbot.dll
struct RobotEn:public RobotBase{
//........
};
//主程序
void main()
{
const char* currentLanguage="en.dll";
RobotBase *pBot;
HMODULE hDll=LoadPlugins(currentLanguage);
pBot=CreateBotFromModel(hdll);//从dll的导出函数建立一个Robot对象并返回.
pBot->talk();
}

dll名字存放于配置文件,主程序即可通用各种语言。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-8 23:35

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表