Elasticsearch 设计模式—建造者模式

本贴最后更新于 2568 天前,其中的信息可能已经时移世易

Elasticsearch 设计模式—建造者模式

建造者模式

构造者模式,或称建造者模式(Builder Pattern).

概念

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 [构建与表示分离,同构建不同表示]

UML 图

构造者模式

一般来说, 构造者模式会涉及到四个角色:

  • 抽象建造者(Builder)角色

      给 出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者 (ConcreteBuilder)角色
    
  • 具体建造者(ConcreteBuilder)角色

      担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例
    
  • 导演者(Director)角色

      担任这个角色的类调用具体建造者角色以创建产品对象。应当指出的是,导演者角色并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者角色
    
  • 产品(Product)角色

      产品便是建造中的复杂对象。一般来说,一个系统中会有多于一个的产品类,而且这些产品类并不一定有共同的接口,而完全可以是不相关联的。
    

Elasticsearch 对于建造者模式的应用

众所周知,Elasticsearch 提供了及其丰富的查询 API,而这些查询 API 的构建核心就是利用建造者模式来完成的,可以说 Elasticsearch 是我见过将建造者模式应用的最好
和最丰富最完美的一个架构.接下来我们就看看 Elasticsearch 是如何在自己的系统里使用建造者模式.

抽象建造者(Builder)角色

QueryBuilder 是构建所有查询的入口,源码如下:

public interface QueryBuilder extends NamedWriteable, ToXContentObject {

    /**
     * Converts this QueryBuilder to a lucene {@link Query}.
     * Returns <tt>null</tt> if this query should be ignored in the context of
     * parent queries.
     *
     * @param context additional information needed to construct the queries
     * @return the {@link Query} or <tt>null</tt> if this query should be ignored upstream
     */
    Query toQuery(QueryShardContext context) throws IOException;

    /**
     * Converts this QueryBuilder to an unscored lucene {@link Query} that acts as a filter.
     * Returns <tt>null</tt> if this query should be ignored in the context of
     * parent queries.
     *
     * @param context additional information needed to construct the queries
     * @return the {@link Query} or <tt>null</tt> if this query should be ignored upstream
     */
    Query toFilter(QueryShardContext context) throws IOException;

    /**
     * Sets the arbitrary name to be assigned to the query (see named queries).
     * Implementers should return the concrete type of the
     * {@link QueryBuilder} so that calls can be chained. This is done
     * automatically when extending {@link AbstractQueryBuilder}.
     */
    QueryBuilder queryName(String queryName);

    /**
     * Returns the arbitrary name assigned to the query (see named queries).
     */
    String queryName();

    /**
     * Returns the boost for this query.
     */
    float boost();

    /**
     * Sets the boost for this query.  Documents matching this query will (in addition to the normal
     * weightings) have their score multiplied by the boost provided.
     * Implementers should return the concrete type of the
     * {@link QueryBuilder} so that calls can be chained. This is done
     * automatically when extending {@link AbstractQueryBuilder}.
     */
    QueryBuilder boost(float boost);

    /**
     * Returns the name that identifies uniquely the query
     */
    String getName();

    /**
     * Rewrites this query builder into its primitive form. By default this method return the builder itself. If the builder
     * did not change the identity reference must be returned otherwise the builder will be rewritten infinitely.
     */
    default QueryBuilder rewrite(QueryRewriteContext queryShardContext) throws IOException {
        return this;
    }

    /**
     * Rewrites the given query into its primitive form. Queries that for instance fetch resources from remote hosts or
     * can simplify / optimize itself should do their heavy lifting during {@link #rewrite(QueryRewriteContext)}. This method
     * rewrites the query until it doesn't change anymore.
     * @throws IOException if an {@link IOException} occurs
     */
    static QueryBuilder rewriteQuery(QueryBuilder original, QueryRewriteContext context) throws IOException {
        QueryBuilder builder = original;
        for (QueryBuilder rewrittenBuilder = builder.rewrite(context); rewrittenBuilder != builder;
             rewrittenBuilder = builder.rewrite(context)) {
            builder = rewrittenBuilder;
        }
        return builder;
    }

}

该接口用来规范定义想要构造一个查询构造器的标准条件,是一个最顶层的抽象构造器接口:

  • boost
  • queryName

而其子类 AbstractQueryBuilder 则是默认实现了接口中的一些规范,两者共同充当建造者模式中的抽象建造者(Builder)角色.

具体建造者(ConcreteBuilder)角色

Elasticsearch 中拥有大量的具体的建造者角色,这些具体的建造者角色就是 Elasticsearch 根据不同的查询场景的构造者,比如:BoolQueryBuilder,MatchPhraseQueryBuilder 等

这里只拿 BoolQueryBuilder 进行说明,部分源码如下:

public BoolQueryBuilder must(QueryBuilder queryBuilder) {
        if (queryBuilder == null) {
            throw new IllegalArgumentException("inner bool query clause cannot be null");
        }
        mustClauses.add(queryBuilder);
        return this;
    }
     public BoolQueryBuilder filter(QueryBuilder queryBuilder) {
            if (queryBuilder == null) {
                throw new IllegalArgumentException("inner bool query clause cannot be null");
            }
            filterClauses.add(queryBuilder);
            return this;
        }
         public BoolQueryBuilder mustNot(QueryBuilder queryBuilder) {
                if (queryBuilder == null) {
                    throw new IllegalArgumentException("inner bool query clause cannot be null");
                }
                mustNotClauses.add(queryBuilder);
                return this;
            }
            public BoolQueryBuilder should(QueryBuilder queryBuilder) {
                    if (queryBuilder == null) {
                        throw new IllegalArgumentException("inner bool query clause cannot be null");
                    }
                    shouldClauses.add(queryBuilder);
                    return this;
                }

从源码中可以看到, 针对不同的条件,可以使用不同的方法进行构造布尔查询。

导演者(Director)角色

Elasticsearch 中充当导演者角色的类是 SearchRequestBuilder,其自身也是一个构造器,同时也是一个导演者(Director)角色,内部持有具体的构造器来完成
具体的构建过程.

产品(Director)角色

Elasticsearch 中充当导演者角色的就是这些构造器本身

参考

  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...