1

Introduction:

I've built a class hierarchy for database filters:

class Filter {
}

class PropertyFilter<T> extends Filter {
  Boolean exists;
  T equal;
}

class ComparableFilter<T> extends PropertyFilter<T> {
  T greaterThan;
  T lessThan;
}

This way I can use PropertyFilter<String> for Strings and ComparableFilter<DateTime> for DateTime objects.


The tricky part:

I have some filter query builder that follows the same hierarchy as the filter classes. Each party of the query builder should add only the additional queries.

Example:

class FilterQueryBuilder<T extends Filter> {
  protected final T filter;      

  public FilterQueryBuilder(T filter) {
    this.filter = filter;
  }

  public Query getQuery() {
    return new Query();
  }
}

class PropertyFilterQueryBuilder<T extends PropertyFilter<?>> extends FilterQueryBuilder<T> {
  public PropertyFilterQueryBuilder(T filter) {
    super(filter);
  }

  public Query getQuery() {
    Query query = super.getQuery();
    if(filter.exists != null) addExistsQuery(query, filter.exists);
    if(filter.equal != null) addEqualQuery(query, filter.equal);
    return query;
  }
}

class ComparableFilterQueryBuilder<T extends ComparableFilter<?>> extends PropertyFilterQueryBuilder<T> {
  public ComparableFilterQueryBuilder(T filter) {
    super(filter);
  }

  public Query getQuery() {
    Query query = super.getQuery();
    if(filter.greaterThan != null) addGreaterThanQuery(query, filter.greaterThan);
    if(filter.lessThan != null) addLessThanQuery(query, filter.lessThan);
    return query;
  }
}

Problem / Questions:

As you can see, I always have to call super.getQuery() and then return the modified query object.

It would be much easier if each class only has a addFiltersToQuery method like this:

addFiltersToQuery() {
  if(filter.x != null) addXQuery(query, filter.x);
  if(filter.y != null) addYQuery(query, filter.y);
}

But of course I'd override this method with my class hierarchy. Though if I call addFiltersToQuery inside the FilterQueryBuilder class, I would only get the latest implementation.

Is there any way to call all implementation without using super?

Benjamin M
  • 23,599
  • 32
  • 121
  • 201

1 Answers1

0

I now built some way to work around this:

class FilterQueryBuilder<T extends Filter> {
  private final T filter;
  private List<Consumer<Query>> queryConsumers = new ArrayList<>();

  public FilterQueryBuilder(T filter) {
    this.filter = filter;
  }

  protected void addQueryConsumer(Consumer<Query> consumer) {
     queryConsumers.add(consumer);
  }

  public Query getQuery() {
    Query query = new Query();
    queryConsumers.forEach(c -> c.accept(query));
    return query;
  }
}

class PropertyFilterQueryBuilder<T extends PropertyFilter<?>> extends FilterQueryBuilder<T> {
  public PropertyFilterQueryBuilder(T filter) {
    super(filter);
    addQueryConsumer(query -> {
      if(filter.exists != null) addExistsQuery(query, filter.exists);
      if(filter.equal != null) addEqualQuery(query, filter.equal);
    });
  }
}

class ComparableFilterQueryBuilder<T extends ComparableFilter<?>> extends PropertyFilterQueryBuilder<T> {
  public ComparableFilterQueryBuilder(T filter) {
    super(filter);
    addQueryConsumer(query -> {
      if(filter.greaterThan != null) addGreaterThanQuery(query, filter.greaterThan);
      if(filter.lessThan != null) addLessThanQuery(query, filter.lessThan);
    });
  }
}

Each class writes its query additions into the queryConsumers array, and the FilterQueryBuilder class applies all those consumers.

Benjamin M
  • 23,599
  • 32
  • 121
  • 201