很长一段时间以来,我一直在研究一个应用程序。由于编程只是一种爱好,这个项目已经花费了太长时间,但这不是重点。我现在正处于每个“问题”都变得非常难以解决的地步。而且我正在考虑重构代码,但这会导致“完全”重写。
让我解释一下这个问题,以及我目前是如何解决的。基本上我有数据,我让事情发生在这些数据上(好吧,我描述了每个程序,不是吗?)。会发生什么:
数据 -> 要求查看器显示 -> 查看器根据实际数据显示数据 查看器返回用户输入 -> 数据 -> 要求“执行者”执行它 -> 新数据
现在这曾经工作得很好,我最初在想“嘿,我可能会通过 qt 或 windows 更改命令提示符 - 甚至可以使用外部 (C#) 并简单地调用这个程序”。
然而,随着程序的发展,它变得越来越令人厌烦。最重要的是,数据以不同的方式显示,具体取决于数据的内容以及 - 更重要的是 - 它所在的位置。所以我回到树并添加了某种方式来“跟踪”父行是什么”。然后一般查看器将搜索最具体的实际小部件。它使用具有 [location; widget] 值的列表,并找到最佳匹配位置。
问题在更新新“数据”时开始 - 我必须检查所有资产 - 查看器,保护程序等。更新检查机制给了我很多错误..诸如“嘿为什么它显示错误的小部件现在又来了?”。
现在我可以完全交换这个了。而不是调用通用查看器的树数据结构。我会使用面向对象的“内部”树功能。这些节点将是子节点(并且当需要新的查看器或保存机制时,会形成一个新的子节点)。
这将消除困难的检查机制,在那里我检查树中的位置。然而,它可能会打开另一罐蠕虫。我想对此发表一些评论?我是否应该将查看器完全分开 - 难以检查数据?或者新方法更好,但它将数据和执行组合到一个节点中。(因此,如果我想从 qt 更改为 cli/C#,这几乎是不可能的)
我到底应该追求什么方法?还有什么我可以做的吗?为了使查看器保持独立,但又避免必须检查以查看应显示的小部件?
编辑,只是为了展示一些“代码”以及我的程序是如何工作的。不确定这是否有什么好处,正如我已经说过的那样,它已经变成了一堆方法论。
它旨在将几个“游戏制作者项目”合并在一起(因为 GM:studio 奇怪地缺少该功能)。Gamemaker 项目文件只是一组 xml 文件。(主 xml 文件仅包含指向其他 xml 文件的链接,以及每个资源(对象、精灵、声音、房间等)的 xml 文件)。然而,有一些“怪癖”使得无法使用诸如 boost 属性树或 qt 之类的东西进行阅读:1) 属性/子节点的顺序在文件的某些部分非常重要。2) 空白经常被忽略,但在其他方面,保留它非常重要。
话虽如此,节点也有很多点完全相同。就像背景可以有<width>200</width>
,房间也可以有。然而,对于用户来说,他所谈论的宽度非常重要。
无论如何,因此“通用查看器”(AskGUIFn)具有以下类型定义来处理此问题:
typedef int (AskGUIFn::*MemberFn)(const GMProject::pTree& tOut, const GMProject::pTree& tIn, int) const;
typedef std::vector<std::pair<boost::regex, MemberFn> > DisplaySubMap_Ty;
typedef std::map<RESOURCE_TYPES, std::pair<DisplaySubMap_Ty, MemberFn> > DisplayMap_Ty;
其中“GMProject::pTree”是一个树节点,RESOURCE_TYPES 是一个常量,用于跟踪我目前的资源类型(精灵、对象等)。这里的“memberFn”只是加载小部件的东西。(虽然 AskGUIFn 当然不是唯一的通用查看器,但只有在其他“自动”-覆盖、跳过、重命名-处理程序失败时才打开这个查看器)。
现在展示如何初始化这些映射(命名空间“MW”中的所有内容都是一个 qt 小部件):
AskGUIFn::DisplayMap_Ty AskGUIFn::DisplayFunctionMap_INIT() {
DisplayMap_Ty t;
DisplaySubMap_Ty tmp;
tmp.push_back(std::pair<boost::regex, AskGUIFn::MemberFn> (boost::regex("^instances "), &AskGUIFn::ExecuteFn<MW::RoomInstanceDialog>));
tmp.push_back(std::pair<boost::regex, AskGUIFn::MemberFn> (boost::regex("^code $"), &AskGUIFn::ExecuteFn<MW::RoomStringDialog>));
tmp.push_back(std::pair<boost::regex, AskGUIFn::MemberFn> (boost::regex("^(isometric|persistent|showcolour|enableViews|clearViewBackground) $"), &AskGUIFn::ExecuteFn<MW::ResourceBoolDialog>));
//etc etc etc
t[RT_ROOM] = std::pair<DisplaySubMap_Ty, MemberFn> (tmp, &AskGUIFn::ExecuteFn<MW::RoomStdDialog>);
tmp.clear();
//repeat above
t[RT_SPRITE] = std::pair<DisplaySubMap_Ty, MemberFn>(tmp, &AskGUIFn::ExecuteFn<MW::RoomStdDialog>);
//for each resource type.
然后,当树数据结构告诉一般查看器它希望显示时,查看器执行以下功能:
AskGUIFn::MemberFn AskGUIFn::FindFirstMatch() const {
auto map_loc(DisplayFunctionMap.find(res_type));
if (map_loc != DisplayFunctionMap.end()) {
std::string stack(CallStackSerialize());
for (auto iter(map_loc->second.first.begin()); iter != map_loc->second.first.end(); ++iter) {
if (boost::regex_search(stack, iter->first)) {
return iter->second;
}
}
return map_loc->second.second;
}
return BackupScreen;
}
这就是问题开始变得坦率的地方。该CallStackSerialize()
函数依赖于调用堆栈。但是,call_stack 存储在“处理程序”中。我将它存储在那里是因为一切都从处理程序开始。我不确定我应该将这个“call_stack”存储在哪里。引入另一个跟踪正在发生的事情的对象?我尝试了将父节点与节点本身一起存储的路线。(防止需要调用堆栈)。然而,这并不像我希望的那样顺利:每个节点只是有一个包含其子节点的向量。所以使用指针指向父注释是不可能的......(PS:也许我应该在另一个问题中对此进行修改......)