最近三周开发的桌面应用系统
前三周开发了一个桌面应用系统。
本来是不想参与这个小项目,因为这是个典型的“内部使用”一次性软件;需求说不清、也难以理清;麻雀虽小五脏俱全,有数据库存取、系统间导入导出、复杂的界面功能、很可能不断变化的业务功能;只有一点很明确:要求截止到3月15日开发完成,也就是最多三周!
在即将正式开发时,经过多次激烈讨论,要做哪些功能总算弄清楚了,但功能究竟具体是什么样还比较模糊。系统技术方案和编程语言方案了反复改了几次,至少有三次非常大的改变,可以说是由谁主负责就有几个差别很大的解决方案。最后是我参与到该项目的设计编码中(总共2个开发人员),由于我不想让从我出手的软件开发成为垃圾软件,所以变成了由我绝对控制、主力实现的形式。
虽然只有三周开发时间,但还是取得了明显的成绩:系统架构比去年软件更精巧完善、增加了两个通用性很强的插件模块、代码结构也较好。其开发合作方式也值得一提:我负责框架设计和实现,另一个同事负责业务功能的实现。
图1 系统基于插件框架构建
图2 系统分层架构
图3 应用外壳层
图4 内容视图层
图5 业务逻辑层
图6 对话框界面层
图7 数据层
图8 改变通知观察机制(ChangeObserver)
新开发了一个通用应用程序内核模块AppUICore,基于该模块的应用程序代码可减少到50行内、只需要一个类。该模块同时实现了交互式界面布局机制、界面布局与界面内容实现分离。下面是AppUICore的工作流程:
图9 程序启动流程
图10 程序退出流程
最为数据访问代理机制(DataAgent),在本项目中新开发出了数据库访问代理插件,可以按照以前XML访问那样的简化方式操作数据库,这样应用开发程序员就可以不关心数据库或XML实现细节,只需要关心如何操作逻辑数据。例如:
Cx_Ptr TestConfigDB::OpenDatabase()
{
Cx_Interface<Ix_ConfigDBFactory> pIFFactory(CLSID_ConfigDBFactory);
CPPUNIT_ASSERT(pIFFactory.IsNotNull());
return pIFFactory->OpenAccessDB(m_wstrDbFile);
}
void TestConfigDB::testAddRecord()
{
Cx_Interface<Ix_ConfigData> pIFDatabase(OpenDatabase());
CPPUNIT_ASSERT(pIFDatabase.IsNotNull());
CConfigIOSection secRec(pIFDatabase->AddSection(NULL, L"book"));
secRec->SetUInt32(L"id", 2);
secRec->SetString(L"title", L"test1");
}
void TestConfigDB::testSelectSQL()
{
Cx_Interface<Ix_ConfigData> pIFDatabase(OpenDatabase());
CPPUNIT_ASSERT(pIFDatabase.IsNotNull());
CConfigIOSection secRecordset(pIFDatabase->GetSection(
L"SELECT id,title FROM article WHERE id=4"));
for (long iRec = 0; iRec < 99; iRec++)
{
CConfigIOSection secRec(secRecordset.GetSectionByIndex(L"", iRec));
if (!secRec->IsValid())
break;
ULONG nID = secRec->GetUInt32(L"id");
std::wstring wstrName = secRec->GetString(L"title");
CPPUNIT_ASSERT(nID == 4);
}
}
void TestConfigDB::testEditRecord()
{
Cx_Interface<Ix_ConfigData> pIFDatabase(OpenDatabase());
CPPUNIT_ASSERT(pIFDatabase.IsNotNull());
CConfigIOSection secRecordset(pIFDatabase->GetSection(NULL, L"book", L"id", 1));
CConfigIOSection secRec(secRecordset.GetSectionByIndex(L"", 0));
CPPUNIT_ASSERT_EQUAL(1L, secRecordset.GetSectionCount(L""));
secRec->SetString(L"title", L"test3");
}