我们与使用VC6编译器的C实现的旧旧系统一起工作.现在我们正在重构代码.我们也切换到VC9编译器.
我们使用外部专有框架,这也是遗留代码,而不是单元测试.为了使我们的代码单元可以测试,我们为框架类引入了接口和包装(提示:参见Martin Fowler的“使用遗留代码”):
现在我们依赖于接口.包装器调用框架方法,我们可以在单元测试中快乐地使用模拟.
在这里我们来看我们的问题
框架类包含许多需要包装和嘲笑的方法.为了实现这一目标,我们的供应商团队编写了一个使用C宏生成接口,封装和模拟实现的API.
包装头文件示例:
class PlanWrapper : public IPlan { // ... WRP_DECLARE_DEFAULTS(FrameworkPlan); // macro WRP_DECLARE_CSTR_ATTR(FrameworkPlanLabel); // macro // ... };
宏WRP_DECLARE_CSTR_ATTR定义如下:
#define WRP_DECLARE_CSTR_ATTR(AttrName) \ virtual bool set##AttrName (LPCTSTR Value_in); \ virtual bool get##AttrName (CString& Value_out); \ virtual bool unset##AttrName (); \ virtual bool isSet##AttrName ()
包装器cpp文件示例:
#include "StdAfx.h" using namespace SomeNamespace; WRP_IMPLEMENT_MODDICOM_DEFAULTS(FrameworkPlan) WRP_IMPLEMENT_W_CSTR_ATTR (FrameworkPlan,FrameworkType1,FrameworkPlanLabel) // ...
宏WRP_IMPLEMENT_W_CSTR_ATTR的定义如下:
#define WRP_IMPLEMENT_W_CSTR_ATTR(ClassName,AtrTypeObj,AttrName) \ bool ClassName##Wrapper::set##AttrName (LPCTSTR Value_in) { \ AtrTypeObj aValue = Value_in; \ FrameworkLink<ClassName> convertedObj = NULL_LINK; \ framework_cast(convertedObj,m_Object); \ return convertedObj != NULL_LINK ? \ convertedObj->set##AttrName (aValue) : false; \ } // ...
我们有一堆更复杂的东西,但我想你得到的想法.
API的问题是它非常复杂,不可读,不可调试,不可测试.
我们想提出一个更好的机制来实现同样的目标.这个想法是我们使用新的编译器(如高级模板,类型列表,特征等)附带的一些高级功能.
使用模板我们几乎可以实现我们的目标,但我们坚持使用方法名称.我们可以推广类型,但是我们如何处理属性名称?
我们还考虑创建一个自动生成包装器接口mocks代码的工具.然而,我们的外部框架的API非常复杂,并且编写这样的工具将是非常昂贵的.