我负责项目中的关键应用程序。它完成与解析业务消息(传统标准),处理它们然后将一些结果存储在数据库(另一个应用程序选择它)相关的东西。经过一年多的工作(我还有其他应用程序可以照顾),应用程序终于稳定了。我已经引入了严格的TDD政策,我有20%的单元测试覆盖率(感谢Michael Feathers为你的书!),其中大部分是关键部分。我也有一些白盒健身测试(那里涵盖了整个业务场景)。我觉得我无法进一步重构这个应用程序,我可以安全地玩它。它的设计非常糟糕,我想重写它。应用本身大约是20k的具有挑战性的传统C / C ++代码。还有其他的依赖关系,但我管理他们大部分的解耦。
我所拥有的只是Sun C ++编译器,cppunitlite,STLPort和Boost。请不要建议其他技术(没有XML,Java等),因为这不是我的组织中的选项。 我想用现代C ++(也许是元编程...),TDD从开始到结束。
我需要解析大约30种类型的消息。它们每个都由3-10行组成,其中大多数非常相似。 这是万恶之源 - >很多代码重复。 每个msgs都有一个描述如何解析它的类。看一下主继承树:
MSG_A MSG_B
/ \ / \
MSG_A_NEW MSG_A_CNL MSG_B_NEW MSG_B_CNL
两棵树都更深了。 MSG_A_NEW和MSG_B_NEW之间存在非常小的差异。它应该由单个类处理,可以注入一些小的自定义。
我最初计划有一个将被自定义的通用msg类。一些实体(构建器......?)将查看msgs并初始化能够解析msg的正确对象。另一个实体将能够发现它是什么行,并且构建器将使用此信息。我打算编写几个解析器,负责解析一个特定的行。这将允许我在解析不同的消息时重用它。
我努力以优雅和可扩展的方式解决几个挑战。 每种类型的消息:
如果是行,有最小和最大数字 - 有一些必须的线 - 有一些可选的行 - 某些行必须在某些地方(即日期不能在msg类型之前),顺序重要
我需要能够验证消息的格式。
我不确定我是否足够好地解释了设计挑战。我的设计经验非常有限。我已经修了一段时间了,最后我会做一些改变来做一些有趣的编码:)
你有什么高级别的建议?您可以在此说明中识别哪些设计模式?主要设计约束是可维护性和可扩展性,底层性能(我们还有其他瓶颈......)。
答案 0 :(得分:1)
我建议你不从包含公共代码的基类继承你的特定消息处理类:
CommonHandler
^ ^
| | = inheritance
MsgAHandler
^ ^
| |
ANewHandler ACnlHandler
这种方法的可重用性很差:例如,如果你想处理某种需要从A_NEW和A_CNL做事的消息,你最终会很快地进行多重继承。
相反,我会选择一个包含公共代码的类,它调用接口来定制公共代码。像这样:
BasicHandler <>--- IMsgHandler ------------\
1 1 ^ ^ ^ ^ * | ^
| | | | | | = inheritance
MsgAHandler | | ANewHandler 1 |
ACnlHandler HandlerContainer <>-/ <>- = containment
HandlerContainer类可用于将其他处理程序的行为分组在一起。
这个模式叫做'复合',如果我没弄错的话。要创建正确的处理程序实例,您当然需要某种工厂。
祝你好运!答案 1 :(得分:0)
听起来像是一个有趣的挑战。 : - )
您的“初始计划”听起来很不错:将所有消息之间的所有类似处理分解出来,并将它们的代码放在基本消息类中。更改项可以成为虚函数(可能是CheckForRequiredLines
或VerifyLineOrder
),可能使用最常见情况的默认实现。然后为特定的消息类型派生其他类。
很难为这样的设计问题提供通用建议。在我看来,您的主解析器函数对应于Factory Method模式,但这是我唯一可以轻松识别的模式。 (我对设计模式的名称不太熟悉 - 我使用了很多设计模式,但我只是在几年前才知道他们拥有的名字。)
答案 2 :(得分:0)
您可能已经意识到这一点,但为了以防万一......您应该选择/借用Gang of Four design patterns book以获取有关识别和应用适当模式的帮助。这是规范参考,它包含交叉引用和表格,以帮助您确定哪些模式可能适合您的应用程序。基于该描述,这里的人可能很难确定可能对您有帮助的特定模式。
答案 3 :(得分:0)
我建议查看boost提供的库,例如Tuple
或mpl::vector
。这些库允许您创建不相关类型的列表,然后对它们进行操作。非常粗略的想法是,每种消息都有类型的序列:
Seq1 -> MSG_A_NEW, MSG_A_CNL
Seq2 -> MSG_B_NEW, MSG_B_CNL
一旦知道了你的消息类型,就可以使用适当的元组和一个将第一个元组类型应用于数据的函数模板。然后是元组中的下一个条目,依此类推。
这确实假设数据流的布局在编译时是已知的,但它确实具有不为数据结构支付任何运行时开销的优势。