代码之家  ›  专栏  ›  技术社区  ›  Nazgob

设计业务MSGS解析器/从头重写

  •  1
  • Nazgob  · 技术社区  · 16 年前

    我负责我项目中的关键应用程序。它执行与解析业务MSG(传统标准)相关的工作,处理它们,然后将一些结果存储在数据库中(另一个应用程序会收集到这些结果)。经过一年多的工作(我还有其他的应用程序需要关注),这个应用程序终于稳定下来了。我已经引入了严格的TDD政策,我有20%的单元测试覆盖率(感谢迈克尔·费瑟的书!)大部分都在关键部位。我也有一些白盒健康测试(这里介绍了整个业务场景)。我觉得我无法进一步重构这个应用程序,因此我可以放心地努力使用它。它的设计太差了,我想重写一下。应用程序本身是具有挑战性的传统C/C++代码的20K左右。还有其他的依赖关系,但我设法将它们大部分分离。


    我所拥有的是Sun C++编译器、CPUPITITLITE、STLPort和Boost。请不要建议其他技术(没有XML,Java等),因为这不是我的组织中的选项。 我想用现代C++来做它(也许玩元编程……),从开始到结束TDD。

    我需要分析大约30种类型的MSG。每一行由3-10行组成,大多数都很相似。 这是万恶之源——大量代码重复。 每个MSG都有一个类,描述如何解析它。看看主要的继承树:

                                 MSG_A                     MSG_B
                                /     \                   /     \
                        MSG_A_NEW   MSG_A_CNL      MSG_B_NEW   MSG_B_CNL
    

    两棵树都深得多。味精新的和味精新的差别很小。它应该由一个可以注入一些小定制的类来处理。

    我最初计划它有一个将被定制的通用消息类。某些实体(生成器…?)将查看MSG并初始化能够解析MSG的适当对象。另一个实体将能够发现它是哪一行,而这个信息将被生成器使用。我计划编写几个解析器,它们只负责解析一个特定的行。这将允许我在分析不同的MSG时重用它。

    我努力以优雅和可扩展的方式解决几个挑战。 每种类型的消息:

    具有最小和最大行数 -有一些必备的台词 -有一些可选的行 -某些行必须位于某些位置(即日期不能早于消息类型),订单事项

    我需要能够验证MSG的格式。


    我不确定我是否足够好地解释了这里的设计挑战。我的设计经验非常有限。我已经修了一段时间的虫子了,最后我要做些有趣的事情了:)

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

    4 回复  |  直到 14 年前
        1
  •  1
  •   eli    16 年前

    我建议你 要从包含如下公共代码的基类继承特定的消息处理类:

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

    这种方法的可重用性很差:例如,如果您想要处理某种需要从一个新的和一个CNL执行操作的消息,那么您将很快得到多个继承。

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

    
    
    

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

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

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

    祝你好运!

        2
  •  0
  •   Head Geek    16 年前

    听起来像一个有趣的挑战。-)

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

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

        3
  •  0
  •   mbyrne215    16 年前

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

        4
  •  0
  •   Head Geek    16 年前

    例如,我建议查看Boost提供的库 Tuple mpl::vector . 这些库允许您创建一个不相关类型的列表,然后对其进行操作。非常粗略的想法是,对于每种消息类型,您都有一系列类型:

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

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

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