代码之家  ›  专栏  ›  技术社区  ›  Adam Asham

如何编写一个易于理解的搜索函数(/可维护)?也许是模块化的方式?

  •  0
  • Adam Asham  · 技术社区  · 17 年前

    每次我看到搜索函数时,它背后的代码都是一团糟的。几百行,意大利面代码,几乎总是一个巨大的方法。使用一种编程语言(Java/Cy/P/PFET)构造一个大的胖SQL查询。很多,很多,如果有的话。

    一定有比这更优雅的方式来做这个?或者这就是使用rmdbs而不是平面数据结构时得到的结果?

    我会更愿意了解这个话题,甚至买本书。亚当

    4 回复  |  直到 17 年前
        1
  •  2
  •   Mauricio Scheffer    17 年前

    使用 query object pattern . 如果可以,也可以使用ORM,它会使事情变得更简单。 实现细节取决于您的平台和体系结构,但以下是一些示例:

        2
  •  0
  •   Fredrik Mörk    17 年前

    在我当前的项目中,我们使用了Mausch提到的查询对象模式的简化版本。在我们的例子中,我们有一个由字段和值组成的搜索条件对象,并且可以将几个这样的对象添加到一个列表中。我们从一开始就有一个operator属性,但它从未被使用过,所以我们将其移除。标准是否被视为和或取决于所使用的搜索方法(我会说它是和在该项目的95%的情况下)。

    find方法本身对这些信息的作用不大;它们将调用数据库中的存储过程,并将条件作为参数传递。大多数过程都是相当直接的,即使我们有几个过程确实涉及到一些字符串处理来为某些字段解包critera列表。

    从调用者的角度来看,代码可能是这样的(Controller类包装了表示性的东西,比如用可配置的实现*实例化搜索对象,用搜索条件填充它等等):

    CustomerCollection customers = CustomerController.Find(new SearchCriterion("Name", "<the customer name>"));
    

    如果需要多个搜索条件,则可以传递集合。在finder函数内部,代码将循环访问集合,将当前值映射到sqlcommand对象中的适当参数。

    这种方法对我们来说效果相当好。

    *)“可配置的实现”意味着我们已经创建了一个架构,当搜索对象被定义为抽象类时,这些抽象类只定义接口并包含一些通用的前后验证。实际的搜索代码是在单独的decendent类中实现的;这使得我们能够快速创建一个“假数据层”,用于模拟一些单元测试的数据库。

        3
  •  0
  •   bwobbones    17 年前

    你看过Lucene项目吗( http://lucene.apache.org )是吗?它的设计正是为了这个目的。其思想是,您构建并维护一组索引,然后这些索引就可以轻松地进行搜索。生命周期的工作方式如下:

    • 编写一组SQL语句,为数据库的所有可搜索区域编制索引
    • 对完整的数据库运行它们以创建数据的初始索引
    • 每次数据更改时,更新这些索引。

    那么查询语言就简单多了,您的查询变得更有针对性。

    在Hibernate工具套件中有一个很棒的项目叫做Hibernate Search。( http://search.hibernate.org )如果您将Hibernate用作ORM,那么它将为您维护索引。

        4
  •  0
  •   Esko    17 年前

    我已经对这个想法进行了一些修改(因为我一段时间前实际上必须实现类似的想法),并且我得出了这样的结论:有两种方法可以使它既有效又特别可维护。但在讨论这些之前,先来看看历史。

    1。为什么这个问题还存在

    大多数搜索函数都是基于从数据库中导出的算法和技术。SQL最初是在20世纪70年代早期开发的( Wikipedia says 1974 )当时的编程是一种完全不同于今天的野兽,因为每计算一个字节,每一个额外的函数调用都能在出色的性能和崩溃之间产生差异,代码是由在汇编中思考的人编写的……好吧,你明白了。

    问题是,这些技术最初大多是在不改变它们的情况下被带入现代世界(以及为什么要改变它们, 不要修理不坏的东西 )这意味着旧的模式也在蔓延。还有一些情况是,最初的算法由于某种原因被误解了,你最终得到了你现在拥有的东西,比如 slow regular expressions .不过,这里需要强调一点,技术本身也不错,通常只是传统的模式而已!

    2。问题的解决方案

    我最终使用的解决方案是一个混合了 builder pattern query object pattern (已经被莫什联系起来了)。作为一个例子,如果我要构建一个实用的系统来构建SQL查询,它应该是这样的:

    SQL.select("column1", "column2")
       .from("relation")
       .where().valueEquals("column1", "hello")
               .and().valueIsLargerThan("column2", 3)
       .toSQL();
    

    这明显的缺点是构建器模式有点过于冗长的倾向。优点是每个构建步骤(方法)本质上都很小,例如 .valueIsLargerThan("a", x) 可能只是 return columnName + ">=" + x; .这意味着它们很容易进行单元测试,最大的好处之一是它们可以很容易地从外部源(如XML/WhatNot)生成,最显著的是,很容易创建从SQL查询到Lucene查询的转换器。( Lucene已经为这个自动化了Afaik,这只是一个例子 )

    第二个我宁愿用但真正避免的是因为它不安全( 除非您花费大量时间创建元数据助手类 )而建设者是。写一个例子比更详细地解释我的意思要容易,所以:

    import static com.org.whatever.SQL.*;
    
    query(select("column1", "column2"),
          from("relation"),
          where(valueEquals("column1", "hello"), 
                valueIsLargerThan("column2", 3)));
    

    我确实把静态导入计算为一个不利因素,但除此之外,这看起来像是我真正想要使用的东西。