【问题标题】:designing business msgs parser / rewriting from scratch设计业务消息解析器/从头开始重写
【发布时间】:2010-09-19 04:54:07
【问题描述】:

我负责项目中的关键应用。它执行与解析业务消息(传统标准)相关的工作,处理它们,然后将一些结果存储在数据库中(另一个应用程序会选择它)。经过一年多的工作(我还有其他应用程序需要照顾),该应用程序终于稳定了。我引入了严格的 TDD 政策,并且我有 20% 的单元测试覆盖率(感谢 Michael Feathers 的书!),其中大部分都在关键部分。我也有一些白盒健身测试(那里涵盖了整个业务场景)。我觉得我无法进一步重构这个应用程序,我可以放心地使用它。设计的太差了,我想重写。应用程序本身大约有 20k 具有挑战性的遗留 C/C++ 代码。还有其他依赖项,但我设法将它们中的大多数解耦。


我只有 Sun C++ 编译器、cppunitlite、STLPort 和 Boost。请不要建议其他技术(没有 XML、Java 等),因为这不是我的组织中的选项。 我想用现代 C++(也许玩元编程......),从头到尾的 TDD。

我需要解析大约 30 种类型的 msg。它们每个都由 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 类型之前),顺序很重要

我需要能够验证消息的格式。


我不确定我在这里解释的设计挑战是否足够好。我的设计经验非常有限。我已经修复了一段时间的错误,最后我将进行一些更改以进行一些有趣的编码:)

您对此有什么高层次的建议?您可以在此描述中识别哪些设计模式?主要的设计约束是可维护性和可扩展性,性能处于底层(无论如何我们还有其他瓶颈......)。

【问题讨论】:

    标签: c++ design-patterns oop templates


    【解决方案1】:

    我建议您不要从包含以下通用代码的基类继承特定的消息处理类:

    
          CommonHandler
                ^                                   ^
                |                                   |  = inheritance
           MsgAHandler
            ^       ^
            |       |
    ANewHandler     ACnlHandler
    

    这种方法的可重用性很差:例如,如果您想处理某种需要从 A_NEW 和 A_CNL 做事的消息,那么您很快就会得到多重继承。

    相反,我会选择一个包含公共代码的类,该类调用一个接口来自定义该公共代码。像这样的:

    
    
    

    BasicHandler <>--- IMsgHandler ------------\ 1 1 ^ ^ ^ ^ * | ^ | | | | | | = inheritance MsgAHandler | | ANewHandler 1 | ACnlHandler HandlerContainer <>-/ <>- = containment

    HandlerContainer 类可用于将其他处理程序的行为组合在一起。

    如果我没记错的话,这种模式称为“复合”。要创建正确的处理程序实例,您当然需要某种工厂。

    祝你好运!

    【讨论】:

    • 我正在尝试类似的东西,感谢您的提示。顺便说一句,体面的文字符号!
    【解决方案2】:

    我建议查看 boost 提供的库,例如 Tuplempl::vector。这些库允许您创建不相关类型的列表,然后对它们进行操作。非常粗略的想法是,每个消息类型都有类型序列:

    Seq1 -> MSG_A_NEW, MSG_A_CNL
    Seq2 -> MSG_B_NEW, MSG_B_CNL
    

    一旦你知道你的消息类型,你就可以使用适当的元组和一个函数模板,将第一个元组类型应用于数据。然后是元组中的下一个条目,依此类推。

    这确实假设您的数据流的布局在编译时是已知的,但它确实具有您无需为数据结构支付任何运行时开销的优势。

    【讨论】:

    • 我是 Boost 库的忠实拥护者,但对于这个问题,这不是必需的,因为类型 不是 不相关的。
    • 但是 - 也许类型相关只是因为这是 OP 认为可以解决问题的唯一方法!
    【解决方案3】:

    您可能已经意识到这一点,但以防万一……您应该拿起/借用Gang of Four design patterns book 以帮助识别和应用适当的模式。这是规范参考,它包含交叉参考和表格,以帮助您确定哪些模式可能适合您的应用程序。仅根据该描述,这里的人们可能很难确定可能对您有帮助的特定模式。

    【讨论】:

    • ofc 我有那本书,但要充分应用您在其中找到的知识需要大量时间和经验。无论如何,谢谢小费。
    【解决方案4】:

    确实听起来像是一个有趣的挑战。 :-)

    您的“初始计划”听起来不错:排除所有消息之间的所有类似处理,并将它们的代码放在基本消息类中。更改的项目可以成为虚拟函数(例如CheckForRequiredLinesVerifyLineOrder,也许),可能在最常见的情况下具有默认实现。然后为特定的消息类型派生其他类。

    对于这样的设计问题,很难给出通用的建议。在我看来,您的主要解析器函数对应于工厂方法模式,但这是我可以轻松识别的唯一一个。 (我对设计模式的名称不太熟悉——我使用了很多,但几年前我才知道它们个名字。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-10
      • 1970-01-01
      • 2011-01-16
      • 1970-01-01
      • 2022-01-19
      • 1970-01-01
      相关资源
      最近更新 更多